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 'at' iki.fi
40 /// SoundTouch WWW: http://www.surina.net/soundtouch
41 ///
42 ////////////////////////////////////////////////////////////////////////////////
43 //
44 // License :
45 //
46 //  SoundTouch audio processing library
47 //  Copyright (c) Olli Parviainen
48 //
49 //  This library is free software; you can redistribute it and/or
50 //  modify it under the terms of the GNU Lesser General Public
51 //  License as published by the Free Software Foundation; either
52 //  version 2.1 of the License, or (at your option) any later version.
53 //
54 //  This library is distributed in the hope that it will be useful,
55 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
56 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
57 //  Lesser General Public License for more details.
58 //
59 //  You should have received a copy of the GNU Lesser General Public
60 //  License along with this library; if not, write to the Free Software
61 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
62 //
63 ////////////////////////////////////////////////////////////////////////////////
64 
65 #include <assert.h>
66 #include <stdlib.h>
67 #include <memory.h>
68 #include <math.h>
69 #include <stdio.h>
70 
71 #include "SoundTouch.h"
72 #include "TDStretch.h"
73 #include "RateTransposer.h"
74 #include "cpu_detect.h"
75 
76 using namespace soundtouch;
77 
78 /// test if two floating point numbers are equal
79 #define TEST_FLOAT_EQUAL(a, b)  (fabs(a - b) < 1e-10)
80 
81 
82 /// Print library version string for autoconf
soundtouch_ac_test()83 extern "C" void soundtouch_ac_test()
84 {
85     printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
86 }
87 
88 
SoundTouch()89 SoundTouch::SoundTouch()
90 {
91     // Initialize rate transposer and tempo changer instances
92 
93     pRateTransposer = new RateTransposer();
94     pTDStretch = TDStretch::newInstance();
95 
96     setOutPipe(pTDStretch);
97 
98     rate = tempo = 0;
99 
100     virtualPitch =
101     virtualRate =
102     virtualTempo = 1.0;
103 
104     calcEffectiveRateAndTempo();
105 
106     samplesExpectedOut = 0;
107     samplesOutput = 0;
108 
109     channels = 0;
110     bSrateSet = false;
111 }
112 
113 
~SoundTouch()114 SoundTouch::~SoundTouch()
115 {
116     delete pRateTransposer;
117     delete pTDStretch;
118 }
119 
120 
121 /// Get SoundTouch library version string
getVersionString()122 const char *SoundTouch::getVersionString()
123 {
124     static const char *_version = SOUNDTOUCH_VERSION;
125 
126     return _version;
127 }
128 
129 
130 /// Get SoundTouch library version Id
getVersionId()131 uint SoundTouch::getVersionId()
132 {
133     return SOUNDTOUCH_VERSION_ID;
134 }
135 
136 
137 // Sets the number of channels, 1 = mono, 2 = stereo
setChannels(uint numChannels)138 void SoundTouch::setChannels(uint numChannels)
139 {
140     if (!verifyNumberOfChannels(numChannels)) return;
141 
142     channels = numChannels;
143     pRateTransposer->setChannels((int)numChannels);
144     pTDStretch->setChannels((int)numChannels);
145 }
146 
147 
148 // Sets new rate control value. Normal rate = 1.0, smaller values
149 // represent slower rate, larger faster rates.
setRate(double newRate)150 void SoundTouch::setRate(double newRate)
151 {
152     virtualRate = newRate;
153     calcEffectiveRateAndTempo();
154 }
155 
156 
157 // Sets new rate control value as a difference in percents compared
158 // to the original rate (-50 .. +100 %)
setRateChange(double newRate)159 void SoundTouch::setRateChange(double newRate)
160 {
161     virtualRate = 1.0 + 0.01 * newRate;
162     calcEffectiveRateAndTempo();
163 }
164 
165 
166 // Sets new tempo control value. Normal tempo = 1.0, smaller values
167 // represent slower tempo, larger faster tempo.
setTempo(double newTempo)168 void SoundTouch::setTempo(double newTempo)
169 {
170     virtualTempo = newTempo;
171     calcEffectiveRateAndTempo();
172 }
173 
174 
175 // Sets new tempo control value as a difference in percents compared
176 // to the original tempo (-50 .. +100 %)
setTempoChange(double newTempo)177 void SoundTouch::setTempoChange(double newTempo)
178 {
179     virtualTempo = 1.0 + 0.01 * newTempo;
180     calcEffectiveRateAndTempo();
181 }
182 
183 
184 // Sets new pitch control value. Original pitch = 1.0, smaller values
185 // represent lower pitches, larger values higher pitch.
setPitch(double newPitch)186 void SoundTouch::setPitch(double newPitch)
187 {
188     virtualPitch = newPitch;
189     calcEffectiveRateAndTempo();
190 }
191 
192 
193 // Sets pitch change in octaves compared to the original pitch
194 // (-1.00 .. +1.00)
setPitchOctaves(double newPitch)195 void SoundTouch::setPitchOctaves(double newPitch)
196 {
197     virtualPitch = exp(0.69314718056 * newPitch);
198     calcEffectiveRateAndTempo();
199 }
200 
201 
202 // Sets pitch change in semi-tones compared to the original pitch
203 // (-12 .. +12)
setPitchSemiTones(int newPitch)204 void SoundTouch::setPitchSemiTones(int newPitch)
205 {
206     setPitchOctaves((double)newPitch / 12.0);
207 }
208 
209 
setPitchSemiTones(double newPitch)210 void SoundTouch::setPitchSemiTones(double newPitch)
211 {
212     setPitchOctaves(newPitch / 12.0);
213 }
214 
215 
216 // Calculates 'effective' rate and tempo values from the
217 // nominal control values.
calcEffectiveRateAndTempo()218 void SoundTouch::calcEffectiveRateAndTempo()
219 {
220     double oldTempo = tempo;
221     double oldRate = rate;
222 
223     tempo = virtualTempo / virtualPitch;
224     rate = virtualPitch * virtualRate;
225 
226     if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
227     if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
228 
229 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
230     if (rate <= 1.0f)
231     {
232         if (output != pTDStretch)
233         {
234             FIFOSamplePipe *tempoOut;
235 
236             assert(output == pRateTransposer);
237             // move samples in the current output buffer to the output of pTDStretch
238             tempoOut = pTDStretch->getOutput();
239             tempoOut->moveSamples(*output);
240             // move samples in pitch transposer's store buffer to tempo changer's input
241             // deprecated : pTDStretch->moveSamples(*pRateTransposer->getStore());
242 
243             output = pTDStretch;
244         }
245     }
246     else
247 #endif
248     {
249         if (output != pRateTransposer)
250         {
251             FIFOSamplePipe *transOut;
252 
253             assert(output == pTDStretch);
254             // move samples in the current output buffer to the output of pRateTransposer
255             transOut = pRateTransposer->getOutput();
256             transOut->moveSamples(*output);
257             // move samples in tempo changer's input to pitch transposer's input
258             pRateTransposer->moveSamples(*pTDStretch->getInput());
259 
260             output = pRateTransposer;
261         }
262     }
263 }
264 
265 
266 // Sets sample rate.
setSampleRate(uint srate)267 void SoundTouch::setSampleRate(uint srate)
268 {
269     // set sample rate, leave other tempo changer parameters as they are.
270     pTDStretch->setParameters((int)srate);
271     bSrateSet = true;
272 }
273 
274 
275 // Adds 'numSamples' pcs of samples from the 'samples' memory position into
276 // the input of the object.
putSamples(const SAMPLETYPE * samples,uint nSamples)277 void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
278 {
279     if (bSrateSet == false)
280     {
281         ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
282     }
283     else if (channels == 0)
284     {
285         ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
286     }
287 
288     // accumulate how many samples are expected out from processing, given the current
289     // processing setting
290     samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
291 
292 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
293     if (rate <= 1.0f)
294     {
295         // transpose the rate down, output the transposed sound to tempo changer buffer
296         assert(output == pTDStretch);
297         pRateTransposer->putSamples(samples, nSamples);
298         pTDStretch->moveSamples(*pRateTransposer);
299     }
300     else
301 #endif
302     {
303         // evaluate the tempo changer, then transpose the rate up,
304         assert(output == pRateTransposer);
305         pTDStretch->putSamples(samples, nSamples);
306         pRateTransposer->moveSamples(*pTDStretch);
307     }
308 }
309 
310 
311 // Flushes the last samples from the processing pipeline to the output.
312 // Clears also the internal processing buffers.
313 //
314 // Note: This function is meant for extracting the last samples of a sound
315 // stream. This function may introduce additional blank samples in the end
316 // of the sound stream, and thus it's not recommended to call this function
317 // in the middle of a sound stream.
flush()318 void SoundTouch::flush()
319 {
320     int i;
321     int numStillExpected;
322     SAMPLETYPE *buff = new SAMPLETYPE[128 * channels];
323 
324     // how many samples are still expected to output
325     numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput);
326     if (numStillExpected < 0) numStillExpected = 0;
327 
328     memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
329     // "Push" the last active samples out from the processing pipeline by
330     // feeding blank samples into the processing pipeline until new,
331     // processed samples appear in the output (not however, more than
332     // 24ksamples in any case)
333     for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++)
334     {
335         putSamples(buff, 128);
336     }
337 
338     adjustAmountOfSamples(numStillExpected);
339 
340     delete[] buff;
341 
342     // Clear input buffers
343     pTDStretch->clearInput();
344     // yet leave the output intouched as that's where the
345     // flushed samples are!
346 }
347 
348 
349 // Changes a setting controlling the processing system behaviour. See the
350 // 'SETTING_...' defines for available setting ID's.
setSetting(int settingId,int value)351 bool SoundTouch::setSetting(int settingId, int value)
352 {
353     int sampleRate, sequenceMs, seekWindowMs, overlapMs;
354 
355     // read current tdstretch routine parameters
356     pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
357 
358     switch (settingId)
359     {
360         case SETTING_USE_AA_FILTER :
361             // enables / disabless anti-alias filter
362             pRateTransposer->enableAAFilter((value != 0) ? true : false);
363             return true;
364 
365         case SETTING_AA_FILTER_LENGTH :
366             // sets anti-alias filter length
367             pRateTransposer->getAAFilter()->setLength(value);
368             return true;
369 
370         case SETTING_USE_QUICKSEEK :
371             // enables / disables tempo routine quick seeking algorithm
372             pTDStretch->enableQuickSeek((value != 0) ? true : false);
373             return true;
374 
375         case SETTING_SEQUENCE_MS:
376             // change time-stretch sequence duration parameter
377             pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
378             return true;
379 
380         case SETTING_SEEKWINDOW_MS:
381             // change time-stretch seek window length parameter
382             pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
383             return true;
384 
385         case SETTING_OVERLAP_MS:
386             // change time-stretch overlap length parameter
387             pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
388             return true;
389 
390         default :
391             return false;
392     }
393 }
394 
395 
396 // Reads a setting controlling the processing system behaviour. See the
397 // 'SETTING_...' defines for available setting ID's.
398 //
399 // Returns the setting value.
getSetting(int settingId) const400 int SoundTouch::getSetting(int settingId) const
401 {
402     int temp;
403 
404     switch (settingId)
405     {
406         case SETTING_USE_AA_FILTER :
407             return (uint)pRateTransposer->isAAFilterEnabled();
408 
409         case SETTING_AA_FILTER_LENGTH :
410             return pRateTransposer->getAAFilter()->getLength();
411 
412         case SETTING_USE_QUICKSEEK :
413             return (uint)pTDStretch->isQuickSeekEnabled();
414 
415         case SETTING_SEQUENCE_MS:
416             pTDStretch->getParameters(NULL, &temp, NULL, NULL);
417             return temp;
418 
419         case SETTING_SEEKWINDOW_MS:
420             pTDStretch->getParameters(NULL, NULL, &temp, NULL);
421             return temp;
422 
423         case SETTING_OVERLAP_MS:
424             pTDStretch->getParameters(NULL, NULL, NULL, &temp);
425             return temp;
426 
427         case SETTING_NOMINAL_INPUT_SEQUENCE :
428         {
429             int size = pTDStretch->getInputSampleReq();
430 
431 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
432             if (rate <= 1.0)
433             {
434                 // transposing done before timestretch, which impacts latency
435                 return (int)(size * rate + 0.5);
436             }
437 #endif
438             return size;
439         }
440 
441         case SETTING_NOMINAL_OUTPUT_SEQUENCE :
442         {
443             int size = pTDStretch->getOutputBatchSize();
444 
445             if (rate > 1.0)
446             {
447                 // transposing done after timestretch, which impacts latency
448                 return (int)(size / rate + 0.5);
449             }
450             return size;
451         }
452 
453         case SETTING_INITIAL_LATENCY:
454         {
455             double latency = pTDStretch->getLatency();
456             int latency_tr = pRateTransposer->getLatency();
457 
458 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
459             if (rate <= 1.0)
460             {
461                 // transposing done before timestretch, which impacts latency
462                 latency = (latency + latency_tr) * rate;
463             }
464             else
465 #endif
466             {
467                 latency += (double)latency_tr / rate;
468             }
469 
470             return (int)(latency + 0.5);
471         }
472 
473         default :
474             return 0;
475     }
476 }
477 
478 
479 // Clears all the samples in the object's output and internal processing
480 // buffers.
clear()481 void SoundTouch::clear()
482 {
483     samplesExpectedOut = 0;
484     samplesOutput = 0;
485     pRateTransposer->clear();
486     pTDStretch->clear();
487 }
488 
489 
490 /// Returns number of samples currently unprocessed.
numUnprocessedSamples() const491 uint SoundTouch::numUnprocessedSamples() const
492 {
493     FIFOSamplePipe * psp;
494     if (pTDStretch)
495     {
496         psp = pTDStretch->getInput();
497         if (psp)
498         {
499             return psp->numSamples();
500         }
501     }
502     return 0;
503 }
504 
505 
506 /// Output samples from beginning of the sample buffer. Copies requested samples to
507 /// output buffer and removes them from the sample buffer. If there are less than
508 /// 'numsample' samples in the buffer, returns all that available.
509 ///
510 /// \return Number of samples returned.
receiveSamples(SAMPLETYPE * output,uint maxSamples)511 uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples)
512 {
513     uint ret = FIFOProcessor::receiveSamples(output, maxSamples);
514     samplesOutput += (long)ret;
515     return ret;
516 }
517 
518 
519 /// Adjusts book-keeping so that given number of samples are removed from beginning of the
520 /// sample buffer without copying them anywhere.
521 ///
522 /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
523 /// with 'ptrBegin' function.
receiveSamples(uint maxSamples)524 uint SoundTouch::receiveSamples(uint maxSamples)
525 {
526     uint ret = FIFOProcessor::receiveSamples(maxSamples);
527     samplesOutput += (long)ret;
528     return ret;
529 }
530 
531 
532 /// Get ratio between input and output audio durations, useful for calculating
533 /// processed output duration: if you'll process a stream of N samples, then
534 /// you can expect to get out N * getInputOutputSampleRatio() samples.
getInputOutputSampleRatio()535 double SoundTouch::getInputOutputSampleRatio()
536 {
537     return 1.0 / (tempo * rate);
538 }
539