|
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 // Last changed : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $ |
|
45 // File revision : $Revision: 4 $ |
|
46 // |
|
47 // $Id: SoundTouch.cpp 195 2014-04-06 15:57:21Z oparviai $ |
|
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 <stdio.h> |
|
77 |
|
78 #include "SoundTouch.h" |
|
79 #include "TDStretch.h" |
|
80 #include "RateTransposer.h" |
|
81 #include "cpu_detect.h" |
|
82 |
|
83 #ifdef _MSC_VER |
|
84 #include <malloc.h> |
|
85 #define alloca _alloca |
|
86 #endif |
|
87 |
|
88 using namespace soundtouch; |
|
89 |
|
90 /// test if two floating point numbers are equal |
|
91 #define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10) |
|
92 |
|
93 |
|
94 /// Print library version string for autoconf |
|
95 extern "C" void soundtouch_ac_test() |
|
96 { |
|
97 printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION); |
|
98 } |
|
99 |
|
100 |
|
101 SoundTouch::SoundTouch() |
|
102 { |
|
103 // Initialize rate transposer and tempo changer instances |
|
104 |
|
105 pRateTransposer = new RateTransposer(); |
|
106 pTDStretch = TDStretch::newInstance(); |
|
107 |
|
108 setOutPipe(pTDStretch); |
|
109 |
|
110 rate = tempo = 0; |
|
111 |
|
112 virtualPitch = |
|
113 virtualRate = |
|
114 virtualTempo = 1.0; |
|
115 |
|
116 calcEffectiveRateAndTempo(); |
|
117 |
|
118 channels = 0; |
|
119 bSrateSet = false; |
|
120 } |
|
121 |
|
122 |
|
123 |
|
124 SoundTouch::~SoundTouch() |
|
125 { |
|
126 delete pRateTransposer; |
|
127 delete pTDStretch; |
|
128 } |
|
129 |
|
130 |
|
131 |
|
132 /// Get SoundTouch library version string |
|
133 const char *SoundTouch::getVersionString() |
|
134 { |
|
135 static const char *_version = SOUNDTOUCH_VERSION; |
|
136 |
|
137 return _version; |
|
138 } |
|
139 |
|
140 |
|
141 /// Get SoundTouch library version Id |
|
142 uint SoundTouch::getVersionId() |
|
143 { |
|
144 return SOUNDTOUCH_VERSION_ID; |
|
145 } |
|
146 |
|
147 |
|
148 // Sets the number of channels, 1 = mono, 2 = stereo |
|
149 void SoundTouch::setChannels(uint numChannels) |
|
150 { |
|
151 /*if (numChannels != 1 && numChannels != 2) |
|
152 { |
|
153 //ST_THROW_RT_ERROR("Illegal number of channels"); |
|
154 return; |
|
155 }*/ |
|
156 channels = numChannels; |
|
157 pRateTransposer->setChannels((int)numChannels); |
|
158 pTDStretch->setChannels((int)numChannels); |
|
159 } |
|
160 |
|
161 |
|
162 |
|
163 // Sets new rate control value. Normal rate = 1.0, smaller values |
|
164 // represent slower rate, larger faster rates. |
|
165 void SoundTouch::setRate(float newRate) |
|
166 { |
|
167 virtualRate = newRate; |
|
168 calcEffectiveRateAndTempo(); |
|
169 } |
|
170 |
|
171 |
|
172 |
|
173 // Sets new rate control value as a difference in percents compared |
|
174 // to the original rate (-50 .. +100 %) |
|
175 void SoundTouch::setRateChange(float newRate) |
|
176 { |
|
177 virtualRate = 1.0f + 0.01f * newRate; |
|
178 calcEffectiveRateAndTempo(); |
|
179 } |
|
180 |
|
181 |
|
182 |
|
183 // Sets new tempo control value. Normal tempo = 1.0, smaller values |
|
184 // represent slower tempo, larger faster tempo. |
|
185 void SoundTouch::setTempo(float newTempo) |
|
186 { |
|
187 virtualTempo = newTempo; |
|
188 calcEffectiveRateAndTempo(); |
|
189 } |
|
190 |
|
191 |
|
192 |
|
193 // Sets new tempo control value as a difference in percents compared |
|
194 // to the original tempo (-50 .. +100 %) |
|
195 void SoundTouch::setTempoChange(float newTempo) |
|
196 { |
|
197 virtualTempo = 1.0f + 0.01f * newTempo; |
|
198 calcEffectiveRateAndTempo(); |
|
199 } |
|
200 |
|
201 |
|
202 |
|
203 // Sets new pitch control value. Original pitch = 1.0, smaller values |
|
204 // represent lower pitches, larger values higher pitch. |
|
205 void SoundTouch::setPitch(float newPitch) |
|
206 { |
|
207 virtualPitch = newPitch; |
|
208 calcEffectiveRateAndTempo(); |
|
209 } |
|
210 |
|
211 |
|
212 |
|
213 // Sets pitch change in octaves compared to the original pitch |
|
214 // (-1.00 .. +1.00) |
|
215 void SoundTouch::setPitchOctaves(float newPitch) |
|
216 { |
|
217 virtualPitch = (float)exp(0.69314718056f * newPitch); |
|
218 calcEffectiveRateAndTempo(); |
|
219 } |
|
220 |
|
221 |
|
222 |
|
223 // Sets pitch change in semi-tones compared to the original pitch |
|
224 // (-12 .. +12) |
|
225 void SoundTouch::setPitchSemiTones(int newPitch) |
|
226 { |
|
227 setPitchOctaves((float)newPitch / 12.0f); |
|
228 } |
|
229 |
|
230 |
|
231 |
|
232 void SoundTouch::setPitchSemiTones(float newPitch) |
|
233 { |
|
234 setPitchOctaves(newPitch / 12.0f); |
|
235 } |
|
236 |
|
237 |
|
238 // Calculates 'effective' rate and tempo values from the |
|
239 // nominal control values. |
|
240 void SoundTouch::calcEffectiveRateAndTempo() |
|
241 { |
|
242 float oldTempo = tempo; |
|
243 float oldRate = rate; |
|
244 |
|
245 tempo = virtualTempo / virtualPitch; |
|
246 rate = virtualPitch * virtualRate; |
|
247 |
|
248 if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate); |
|
249 if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo); |
|
250 |
|
251 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER |
|
252 if (rate <= 1.0f) |
|
253 { |
|
254 if (output != pTDStretch) |
|
255 { |
|
256 FIFOSamplePipe *tempoOut; |
|
257 |
|
258 assert(output == pRateTransposer); |
|
259 // move samples in the current output buffer to the output of pTDStretch |
|
260 tempoOut = pTDStretch->getOutput(); |
|
261 tempoOut->moveSamples(*output); |
|
262 // move samples in pitch transposer's store buffer to tempo changer's input |
|
263 // deprecated : pTDStretch->moveSamples(*pRateTransposer->getStore()); |
|
264 |
|
265 output = pTDStretch; |
|
266 } |
|
267 } |
|
268 else |
|
269 #endif |
|
270 { |
|
271 if (output != pRateTransposer) |
|
272 { |
|
273 FIFOSamplePipe *transOut; |
|
274 |
|
275 assert(output == pTDStretch); |
|
276 // move samples in the current output buffer to the output of pRateTransposer |
|
277 transOut = pRateTransposer->getOutput(); |
|
278 transOut->moveSamples(*output); |
|
279 // move samples in tempo changer's input to pitch transposer's input |
|
280 pRateTransposer->moveSamples(*pTDStretch->getInput()); |
|
281 |
|
282 output = pRateTransposer; |
|
283 } |
|
284 } |
|
285 } |
|
286 |
|
287 |
|
288 // Sets sample rate. |
|
289 void SoundTouch::setSampleRate(uint srate) |
|
290 { |
|
291 bSrateSet = true; |
|
292 // set sample rate, leave other tempo changer parameters as they are. |
|
293 pTDStretch->setParameters((int)srate); |
|
294 } |
|
295 |
|
296 |
|
297 // Adds 'numSamples' pcs of samples from the 'samples' memory position into |
|
298 // the input of the object. |
|
299 void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples) |
|
300 { |
|
301 if (bSrateSet == false) |
|
302 { |
|
303 ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined"); |
|
304 } |
|
305 else if (channels == 0) |
|
306 { |
|
307 ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined"); |
|
308 } |
|
309 |
|
310 // Transpose the rate of the new samples if necessary |
|
311 /* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value... |
|
312 if (rate == 1.0f) |
|
313 { |
|
314 // The rate value is same as the original, simply evaluate the tempo changer. |
|
315 assert(output == pTDStretch); |
|
316 if (pRateTransposer->isEmpty() == 0) |
|
317 { |
|
318 // yet flush the last samples in the pitch transposer buffer |
|
319 // (may happen if 'rate' changes from a non-zero value to zero) |
|
320 pTDStretch->moveSamples(*pRateTransposer); |
|
321 } |
|
322 pTDStretch->putSamples(samples, nSamples); |
|
323 } |
|
324 */ |
|
325 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER |
|
326 else if (rate <= 1.0f) |
|
327 { |
|
328 // transpose the rate down, output the transposed sound to tempo changer buffer |
|
329 assert(output == pTDStretch); |
|
330 pRateTransposer->putSamples(samples, nSamples); |
|
331 pTDStretch->moveSamples(*pRateTransposer); |
|
332 } |
|
333 else |
|
334 #endif |
|
335 { |
|
336 // evaluate the tempo changer, then transpose the rate up, |
|
337 assert(output == pRateTransposer); |
|
338 pTDStretch->putSamples(samples, nSamples); |
|
339 pRateTransposer->moveSamples(*pTDStretch); |
|
340 } |
|
341 } |
|
342 |
|
343 |
|
344 // Flushes the last samples from the processing pipeline to the output. |
|
345 // Clears also the internal processing buffers. |
|
346 // |
|
347 // Note: This function is meant for extracting the last samples of a sound |
|
348 // stream. This function may introduce additional blank samples in the end |
|
349 // of the sound stream, and thus it's not recommended to call this function |
|
350 // in the middle of a sound stream. |
|
351 void SoundTouch::flush() |
|
352 { |
|
353 int i; |
|
354 int nUnprocessed; |
|
355 int nOut; |
|
356 SAMPLETYPE *buff=(SAMPLETYPE*)alloca(64*channels*sizeof(SAMPLETYPE)); |
|
357 |
|
358 // check how many samples still await processing, and scale |
|
359 // that by tempo & rate to get expected output sample count |
|
360 nUnprocessed = numUnprocessedSamples(); |
|
361 nUnprocessed = (int)((double)nUnprocessed / (tempo * rate) + 0.5); |
|
362 |
|
363 nOut = numSamples(); // ready samples currently in buffer ... |
|
364 nOut += nUnprocessed; // ... and how many we expect there to be in the end |
|
365 |
|
366 memset(buff, 0, 64 * channels * sizeof(SAMPLETYPE)); |
|
367 // "Push" the last active samples out from the processing pipeline by |
|
368 // feeding blank samples into the processing pipeline until new, |
|
369 // processed samples appear in the output (not however, more than |
|
370 // 8ksamples in any case) |
|
371 for (i = 0; i < 128; i ++) |
|
372 { |
|
373 putSamples(buff, 64); |
|
374 if ((int)numSamples() >= nOut) |
|
375 { |
|
376 // Enough new samples have appeared into the output! |
|
377 // As samples come from processing with bigger chunks, now truncate it |
|
378 // back to maximum "nOut" samples to improve duration accuracy |
|
379 adjustAmountOfSamples(nOut); |
|
380 |
|
381 // finish |
|
382 break; |
|
383 } |
|
384 } |
|
385 |
|
386 // Clear working buffers |
|
387 pRateTransposer->clear(); |
|
388 pTDStretch->clearInput(); |
|
389 // yet leave the 'tempoChanger' output intouched as that's where the |
|
390 // flushed samples are! |
|
391 } |
|
392 |
|
393 |
|
394 // Changes a setting controlling the processing system behaviour. See the |
|
395 // 'SETTING_...' defines for available setting ID's. |
|
396 bool SoundTouch::setSetting(int settingId, int value) |
|
397 { |
|
398 int sampleRate, sequenceMs, seekWindowMs, overlapMs; |
|
399 |
|
400 // read current tdstretch routine parameters |
|
401 pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs); |
|
402 |
|
403 switch (settingId) |
|
404 { |
|
405 case SETTING_USE_AA_FILTER : |
|
406 // enables / disabless anti-alias filter |
|
407 pRateTransposer->enableAAFilter((value != 0) ? true : false); |
|
408 return true; |
|
409 |
|
410 case SETTING_AA_FILTER_LENGTH : |
|
411 // sets anti-alias filter length |
|
412 pRateTransposer->getAAFilter()->setLength(value); |
|
413 return true; |
|
414 |
|
415 case SETTING_USE_QUICKSEEK : |
|
416 // enables / disables tempo routine quick seeking algorithm |
|
417 pTDStretch->enableQuickSeek((value != 0) ? true : false); |
|
418 return true; |
|
419 |
|
420 case SETTING_SEQUENCE_MS: |
|
421 // change time-stretch sequence duration parameter |
|
422 pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs); |
|
423 return true; |
|
424 |
|
425 case SETTING_SEEKWINDOW_MS: |
|
426 // change time-stretch seek window length parameter |
|
427 pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs); |
|
428 return true; |
|
429 |
|
430 case SETTING_OVERLAP_MS: |
|
431 // change time-stretch overlap length parameter |
|
432 pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value); |
|
433 return true; |
|
434 |
|
435 default : |
|
436 return false; |
|
437 } |
|
438 } |
|
439 |
|
440 |
|
441 // Reads a setting controlling the processing system behaviour. See the |
|
442 // 'SETTING_...' defines for available setting ID's. |
|
443 // |
|
444 // Returns the setting value. |
|
445 int SoundTouch::getSetting(int settingId) const |
|
446 { |
|
447 int temp; |
|
448 |
|
449 switch (settingId) |
|
450 { |
|
451 case SETTING_USE_AA_FILTER : |
|
452 return (uint)pRateTransposer->isAAFilterEnabled(); |
|
453 |
|
454 case SETTING_AA_FILTER_LENGTH : |
|
455 return pRateTransposer->getAAFilter()->getLength(); |
|
456 |
|
457 case SETTING_USE_QUICKSEEK : |
|
458 return (uint) pTDStretch->isQuickSeekEnabled(); |
|
459 |
|
460 case SETTING_SEQUENCE_MS: |
|
461 pTDStretch->getParameters(NULL, &temp, NULL, NULL); |
|
462 return temp; |
|
463 |
|
464 case SETTING_SEEKWINDOW_MS: |
|
465 pTDStretch->getParameters(NULL, NULL, &temp, NULL); |
|
466 return temp; |
|
467 |
|
468 case SETTING_OVERLAP_MS: |
|
469 pTDStretch->getParameters(NULL, NULL, NULL, &temp); |
|
470 return temp; |
|
471 |
|
472 case SETTING_NOMINAL_INPUT_SEQUENCE : |
|
473 return pTDStretch->getInputSampleReq(); |
|
474 |
|
475 case SETTING_NOMINAL_OUTPUT_SEQUENCE : |
|
476 return pTDStretch->getOutputBatchSize(); |
|
477 |
|
478 default : |
|
479 return 0; |
|
480 } |
|
481 } |
|
482 |
|
483 |
|
484 // Clears all the samples in the object's output and internal processing |
|
485 // buffers. |
|
486 void SoundTouch::clear() |
|
487 { |
|
488 pRateTransposer->clear(); |
|
489 pTDStretch->clear(); |
|
490 } |
|
491 |
|
492 |
|
493 |
|
494 /// Returns number of samples currently unprocessed. |
|
495 uint SoundTouch::numUnprocessedSamples() const |
|
496 { |
|
497 FIFOSamplePipe * psp; |
|
498 if (pTDStretch) |
|
499 { |
|
500 psp = pTDStretch->getInput(); |
|
501 if (psp) |
|
502 { |
|
503 return psp->numSamples(); |
|
504 } |
|
505 } |
|
506 return 0; |
|
507 } |