SoundTouch.cpp (16353B)
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 83 extern "C" void soundtouch_ac_test() 84 { 85 printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION); 86 } 87 88 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 114 SoundTouch::~SoundTouch() 115 { 116 delete pRateTransposer; 117 delete pTDStretch; 118 } 119 120 121 /// Get SoundTouch library version string 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 131 uint SoundTouch::getVersionId() 132 { 133 return SOUNDTOUCH_VERSION_ID; 134 } 135 136 137 // Sets the number of channels, 1 = mono, 2 = stereo 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. 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 %) 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. 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 %) 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. 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) 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) 204 void SoundTouch::setPitchSemiTones(int newPitch) 205 { 206 setPitchOctaves((double)newPitch / 12.0); 207 } 208 209 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. 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. 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. 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. 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. 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. 400 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. 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. 491 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. 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. 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. 535 double SoundTouch::getInputOutputSampleRatio() 536 { 537 return 1.0 / (tempo * rate); 538 }