1 |
william |
15 |
////////////////////////////////////////////////////////////////////////////// |
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 |
|
|
} |