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