add new sigc++2 directory
[ardour.git] / libs / soundtouch / SoundTouch.cpp
1 //////////////////////////////////////////////////////////////////////////////
2 ///
3 /// SoundTouch - main class for tempo/pitch/rate adjusting routines. 
4 ///
5 /// Notes:
6 /// - Initialize the SoundTouch object instance by setting up the sound stream 
7 ///   parameters with functions 'setSampleRate' and 'setChannels', then set 
8 ///   desired tempo/pitch/rate settings with the corresponding functions.
9 ///
10 /// - The SoundTouch class behaves like a first-in-first-out pipeline: The 
11 ///   samples that are to be processed are fed into one of the pipe by calling
12 ///   function 'putSamples', while the ready processed samples can be read 
13 ///   from the other end of the pipeline with function 'receiveSamples'.
14 /// 
15 /// - The SoundTouch processing classes require certain sized 'batches' of 
16 ///   samples in order to process the sound. For this reason the classes buffer 
17 ///   incoming samples until there are enough of samples available for 
18 ///   processing, then they carry out the processing step and consequently
19 ///   make the processed samples available for outputting.
20 /// 
21 /// - For the above reason, the processing routines introduce a certain 
22 ///   'latency' between the input and output, so that the samples input to
23 ///   SoundTouch may not be immediately available in the output, and neither 
24 ///   the amount of outputtable samples may not immediately be in direct 
25 ///   relationship with the amount of previously input samples.
26 ///
27 /// - The tempo/pitch/rate control parameters can be altered during processing.
28 ///   Please notice though that they aren't currently protected by semaphores,
29 ///   so in multi-thread application external semaphore protection may be
30 ///   required.
31 ///
32 /// - This class utilizes classes 'TDStretch' for tempo change (without modifying
33 ///   pitch) and 'RateTransposer' for changing the playback rate (that is, both 
34 ///   tempo and pitch in the same ratio) of the sound. The third available control 
35 ///   'pitch' (change pitch but maintain tempo) is produced by a combination of
36 ///   combining the two other controls.
37 ///
38 /// Author        : Copyright (c) Olli Parviainen
39 /// Author e-mail : oparviai @ iki.fi
40 /// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
41 ///
42 ////////////////////////////////////////////////////////////////////////////////
43 //
44 // Last changed  : $Date$
45 // File revision : $Revision$
46 //
47 // $Id$
48 //
49 ////////////////////////////////////////////////////////////////////////////////
50 //
51 // License :
52 //
53 //  SoundTouch audio processing library
54 //  Copyright (c) Olli Parviainen
55 //
56 //  This library is free software; you can redistribute it and/or
57 //  modify it under the terms of the GNU Lesser General Public
58 //  License as published by the Free Software Foundation; either
59 //  version 2.1 of the License, or (at your option) any later version.
60 //
61 //  This library is distributed in the hope that it will be useful,
62 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
63 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
64 //  Lesser General Public License for more details.
65 //
66 //  You should have received a copy of the GNU Lesser General Public
67 //  License along with this library; if not, write to the Free Software
68 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
69 //
70 ////////////////////////////////////////////////////////////////////////////////
71
72 #include <assert.h>
73 #include <stdlib.h>
74 #include <memory.h>
75 #include <math.h>
76 #include <stdexcept>
77 #include <stdio.h>
78
79 #include "SoundTouch.h"
80 #include "TDStretch.h"
81 #include "RateTransposer.h"
82 #include "cpu_detect.h"
83
84 using namespace soundtouch;
85
86 /// Print library version string
87 extern "C" void soundtouch_ac_test()
88 {
89     printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
90
91
92
93 SoundTouch::SoundTouch()
94 {
95     // Initialize rate transposer and tempo changer instances
96
97     pRateTransposer = RateTransposer::newInstance();
98     pTDStretch = TDStretch::newInstance();
99
100     setOutPipe(pTDStretch);
101
102     rate = tempo = 0;
103
104     virtualPitch = 
105     virtualRate = 
106     virtualTempo = 1.0;
107
108     calcEffectiveRateAndTempo();
109
110     channels = 0;
111     bSrateSet = FALSE;
112 }
113
114
115
116 SoundTouch::~SoundTouch()
117 {
118     delete pRateTransposer;
119     delete pTDStretch;
120 }
121
122
123
124 /// Get SoundTouch library version string
125 const char *SoundTouch::getVersionString()
126 {
127     static const char *_version = SOUNDTOUCH_VERSION;
128
129     return _version;
130 }
131
132
133 /// Get SoundTouch library version Id
134 uint SoundTouch::getVersionId()
135 {
136     return SOUNDTOUCH_VERSION_ID;
137 }
138
139
140 // Sets the number of channels, 1 = mono, 2 = stereo
141 void SoundTouch::setChannels(uint numChannels)
142 {
143     if (numChannels != 1 && numChannels != 2) 
144     {
145         throw std::runtime_error("Illegal number of channels");
146     }
147     channels = numChannels;
148     pRateTransposer->setChannels(numChannels);
149     pTDStretch->setChannels(numChannels);
150 }
151
152
153
154 // Sets new rate control value. Normal rate = 1.0, smaller values
155 // represent slower rate, larger faster rates.
156 void SoundTouch::setRate(float newRate)
157 {
158     virtualRate = newRate;
159     calcEffectiveRateAndTempo();
160 }
161
162
163
164 // Sets new rate control value as a difference in percents compared
165 // to the original rate (-50 .. +100 %)
166 void SoundTouch::setRateChange(float newRate)
167 {
168     virtualRate = 1.0f + 0.01f * newRate;
169     calcEffectiveRateAndTempo();
170 }
171
172
173
174 // Sets new tempo control value. Normal tempo = 1.0, smaller values
175 // represent slower tempo, larger faster tempo.
176 void SoundTouch::setTempo(float newTempo)
177 {
178     virtualTempo = newTempo;
179     calcEffectiveRateAndTempo();
180 }
181
182
183
184 // Sets new tempo control value as a difference in percents compared
185 // to the original tempo (-50 .. +100 %)
186 void SoundTouch::setTempoChange(float newTempo)
187 {
188     virtualTempo = 1.0f + 0.01f * newTempo;
189     calcEffectiveRateAndTempo();
190 }
191
192
193
194 // Sets new pitch control value. Original pitch = 1.0, smaller values
195 // represent lower pitches, larger values higher pitch.
196 void SoundTouch::setPitch(float newPitch)
197 {
198     virtualPitch = newPitch;
199     calcEffectiveRateAndTempo();
200 }
201
202
203
204 // Sets pitch change in octaves compared to the original pitch
205 // (-1.00 .. +1.00)
206 void SoundTouch::setPitchOctaves(float newPitch)
207 {
208     virtualPitch = (float)exp(0.69314718056f * newPitch);
209     calcEffectiveRateAndTempo();
210 }
211
212
213
214 // Sets pitch change in semi-tones compared to the original pitch
215 // (-12 .. +12)
216 void SoundTouch::setPitchSemiTones(int newPitch)
217 {
218     setPitchOctaves((float)newPitch / 12.0f);
219 }
220
221
222
223 void SoundTouch::setPitchSemiTones(float newPitch)
224 {
225     setPitchOctaves(newPitch / 12.0f);
226 }
227
228
229 // Calculates 'effective' rate and tempo values from the
230 // nominal control values.
231 void SoundTouch::calcEffectiveRateAndTempo()
232 {
233     float oldTempo = tempo;
234     float oldRate = rate;
235
236     tempo = virtualTempo / virtualPitch;
237     rate = virtualPitch * virtualRate;
238
239     if (rate != oldRate) pRateTransposer->setRate(rate);
240     if (tempo != oldTempo) pTDStretch->setTempo(tempo);
241
242     if (rate > 1.0f) 
243     {
244         if (output != pRateTransposer) 
245         {
246             FIFOSamplePipe *transOut;
247
248             assert(output == pTDStretch);
249             // move samples in the current output buffer to the output of pRateTransposer
250             transOut = pRateTransposer->getOutput();
251             transOut->moveSamples(*output);
252             // move samples in tempo changer's input to pitch transposer's input
253             pRateTransposer->moveSamples(*pTDStretch->getInput());
254
255             output = pRateTransposer;
256         }
257     } 
258     else 
259     {
260         if (output != pTDStretch) 
261         {
262             FIFOSamplePipe *tempoOut;
263
264             assert(output == pRateTransposer);
265             // move samples in the current output buffer to the output of pTDStretch
266             tempoOut = pTDStretch->getOutput();
267             tempoOut->moveSamples(*output);
268             // move samples in pitch transposer's store buffer to tempo changer's input
269             pTDStretch->moveSamples(*pRateTransposer->getStore());
270
271             output = pTDStretch;
272
273         }
274     }
275 }
276
277
278 // Sets sample rate.
279 void SoundTouch::setSampleRate(uint srate)
280 {
281     bSrateSet = TRUE;
282     // set sample rate, leave other tempo changer parameters as they are.
283     pTDStretch->setParameters(srate);
284 }
285
286
287 // Adds 'numSamples' pcs of samples from the 'samples' memory position into
288 // the input of the object.
289 void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples)
290 {
291     if (bSrateSet == FALSE) 
292     {
293         throw std::runtime_error("SoundTouch : Sample rate not defined");
294     } 
295     else if (channels == 0) 
296     {
297         throw std::runtime_error("SoundTouch : Number of channels not defined");
298     }
299
300     // Transpose the rate of the new samples if necessary
301     if (rate == 1.0f) 
302     {
303         // The rate value is same as the original, simply evaluate the tempo changer. 
304         assert(output == pTDStretch);
305         if (pRateTransposer->isEmpty() == 0) 
306         {
307             // yet flush the last samples in the pitch transposer buffer
308             // (may happen if 'rate' changes from a non-zero value to zero)
309             pTDStretch->moveSamples(*pRateTransposer);
310         }
311         pTDStretch->putSamples(samples, numSamples);
312     } 
313     else if (rate < 1.0f) 
314     {
315         // transpose the rate down, output the transposed sound to tempo changer buffer
316         assert(output == pTDStretch);
317         pRateTransposer->putSamples(samples, numSamples);
318         pTDStretch->moveSamples(*pRateTransposer);
319     } 
320     else 
321     {
322         assert(rate > 1.0f);
323         // evaluate the tempo changer, then transpose the rate up, 
324         assert(output == pRateTransposer);
325         pTDStretch->putSamples(samples, numSamples);
326         pRateTransposer->moveSamples(*pTDStretch);
327     }
328 }
329
330
331 // Flushes the last samples from the processing pipeline to the output.
332 // Clears also the internal processing buffers.
333 //
334 // Note: This function is meant for extracting the last samples of a sound
335 // stream. This function may introduce additional blank samples in the end
336 // of the sound stream, and thus it's not recommended to call this function
337 // in the middle of a sound stream.
338 void SoundTouch::flush()
339 {
340     int i;
341     uint nOut;
342     SAMPLETYPE buff[128];
343
344     nOut = numSamples();
345
346     memset(buff, 0, 128 * sizeof(SAMPLETYPE));
347     // "Push" the last active samples out from the processing pipeline by
348     // feeding blank samples into the processing pipeline until new, 
349     // processed samples appear in the output (not however, more than 
350     // 8ksamples in any case)
351     for (i = 0; i < 128; i ++) 
352     {
353         putSamples(buff, 64);
354         if (numSamples() != nOut) break;  // new samples have appeared in the output!
355     }
356
357     // Clear working buffers
358     pRateTransposer->clear();
359     pTDStretch->clearInput();
360     // yet leave the 'tempoChanger' output intouched as that's where the
361     // flushed samples are!
362 }
363
364
365 // Changes a setting controlling the processing system behaviour. See the
366 // 'SETTING_...' defines for available setting ID's.
367 BOOL SoundTouch::setSetting(uint settingId, uint value)
368 {
369     uint sampleRate, sequenceMs, seekWindowMs, overlapMs;
370
371     // read current tdstretch routine parameters
372     pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
373
374     switch (settingId) 
375     {
376         case SETTING_USE_AA_FILTER :
377             // enables / disabless anti-alias filter
378             pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE);
379             return TRUE;
380
381         case SETTING_AA_FILTER_LENGTH :
382             // sets anti-alias filter length
383             pRateTransposer->getAAFilter()->setLength(value);
384             return TRUE;
385
386         case SETTING_USE_QUICKSEEK :
387             // enables / disables tempo routine quick seeking algorithm
388             pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE);
389             return TRUE;
390
391         case SETTING_SEQUENCE_MS:
392             // change time-stretch sequence duration parameter
393             pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
394             return TRUE;
395
396         case SETTING_SEEKWINDOW_MS:
397             // change time-stretch seek window length parameter
398             pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
399             return TRUE;
400
401         case SETTING_OVERLAP_MS:
402             // change time-stretch overlap length parameter
403             pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
404             return TRUE;
405
406         default :
407             return FALSE;
408     }
409 }
410
411
412 // Reads a setting controlling the processing system behaviour. See the
413 // 'SETTING_...' defines for available setting ID's.
414 //
415 // Returns the setting value.
416 uint SoundTouch::getSetting(uint settingId) const
417 {
418     uint temp;
419
420     switch (settingId) 
421     {
422         case SETTING_USE_AA_FILTER :
423             return pRateTransposer->isAAFilterEnabled();
424
425         case SETTING_AA_FILTER_LENGTH :
426             return pRateTransposer->getAAFilter()->getLength();
427
428         case SETTING_USE_QUICKSEEK :
429             return pTDStretch->isQuickSeekEnabled();
430
431         case SETTING_SEQUENCE_MS:
432             pTDStretch->getParameters(NULL, &temp, NULL, NULL);
433             return temp;
434
435         case SETTING_SEEKWINDOW_MS:
436             pTDStretch->getParameters(NULL, NULL, &temp, NULL);
437             return temp;
438
439         case SETTING_OVERLAP_MS:
440             pTDStretch->getParameters(NULL, NULL, NULL, &temp);
441             return temp;
442
443         default :
444             return 0;
445     }
446 }
447
448
449 // Clears all the samples in the object's output and internal processing
450 // buffers.
451 void SoundTouch::clear()
452 {
453     pRateTransposer->clear();
454     pTDStretch->clear();
455 }
456
457
458
459 /// Returns number of samples currently unprocessed.
460 uint SoundTouch::numUnprocessedSamples() const
461 {
462     FIFOSamplePipe * psp;
463     if (pTDStretch)
464     {
465         psp = pTDStretch->getInput();
466         if (psp)
467         {
468             return psp->numSamples();
469         }
470     }
471     return 0;
472 }