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