media/libsoundtouch/src/TDStretch.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 ////////////////////////////////////////////////////////////////////////////////
michael@0 2 ///
michael@0 3 /// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
michael@0 4 /// while maintaining the original pitch by using a time domain WSOLA-like
michael@0 5 /// method with several performance-increasing tweaks.
michael@0 6 ///
michael@0 7 /// Note : MMX optimized functions reside in a separate, platform-specific
michael@0 8 /// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
michael@0 9 ///
michael@0 10 /// Author : Copyright (c) Olli Parviainen
michael@0 11 /// Author e-mail : oparviai 'at' iki.fi
michael@0 12 /// SoundTouch WWW: http://www.surina.net/soundtouch
michael@0 13 ///
michael@0 14 ////////////////////////////////////////////////////////////////////////////////
michael@0 15 //
michael@0 16 // Last changed : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
michael@0 17 // File revision : $Revision: 1.12 $
michael@0 18 //
michael@0 19 // $Id: TDStretch.cpp 195 2014-04-06 15:57:21Z oparviai $
michael@0 20 //
michael@0 21 ////////////////////////////////////////////////////////////////////////////////
michael@0 22 //
michael@0 23 // License :
michael@0 24 //
michael@0 25 // SoundTouch audio processing library
michael@0 26 // Copyright (c) Olli Parviainen
michael@0 27 //
michael@0 28 // This library is free software; you can redistribute it and/or
michael@0 29 // modify it under the terms of the GNU Lesser General Public
michael@0 30 // License as published by the Free Software Foundation; either
michael@0 31 // version 2.1 of the License, or (at your option) any later version.
michael@0 32 //
michael@0 33 // This library is distributed in the hope that it will be useful,
michael@0 34 // but WITHOUT ANY WARRANTY; without even the implied warranty of
michael@0 35 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
michael@0 36 // Lesser General Public License for more details.
michael@0 37 //
michael@0 38 // You should have received a copy of the GNU Lesser General Public
michael@0 39 // License along with this library; if not, write to the Free Software
michael@0 40 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
michael@0 41 //
michael@0 42 ////////////////////////////////////////////////////////////////////////////////
michael@0 43
michael@0 44 #include <string.h>
michael@0 45 #include <limits.h>
michael@0 46 #include <assert.h>
michael@0 47 #include <math.h>
michael@0 48 #include <float.h>
michael@0 49
michael@0 50 #include "STTypes.h"
michael@0 51 #include "cpu_detect.h"
michael@0 52 #include "TDStretch.h"
michael@0 53
michael@0 54 using namespace soundtouch;
michael@0 55
michael@0 56 #define max(x, y) (((x) > (y)) ? (x) : (y))
michael@0 57
michael@0 58
michael@0 59 /*****************************************************************************
michael@0 60 *
michael@0 61 * Constant definitions
michael@0 62 *
michael@0 63 *****************************************************************************/
michael@0 64
michael@0 65 // Table for the hierarchical mixing position seeking algorithm
michael@0 66 static const short _scanOffsets[5][24]={
michael@0 67 { 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
michael@0 68 868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
michael@0 69 {-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
michael@0 70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
michael@0 71 { -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
michael@0 72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
michael@0 73 { -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
michael@0 74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
michael@0 75 { 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111,
michael@0 76 116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}};
michael@0 77
michael@0 78 /*****************************************************************************
michael@0 79 *
michael@0 80 * Implementation of the class 'TDStretch'
michael@0 81 *
michael@0 82 *****************************************************************************/
michael@0 83
michael@0 84
michael@0 85 TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
michael@0 86 {
michael@0 87 bQuickSeek = false;
michael@0 88 channels = 2;
michael@0 89
michael@0 90 pMidBuffer = NULL;
michael@0 91 pMidBufferUnaligned = NULL;
michael@0 92 overlapLength = 0;
michael@0 93
michael@0 94 bAutoSeqSetting = true;
michael@0 95 bAutoSeekSetting = true;
michael@0 96
michael@0 97 // outDebt = 0;
michael@0 98 skipFract = 0;
michael@0 99
michael@0 100 tempo = 1.0f;
michael@0 101 setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
michael@0 102 setTempo(1.0f);
michael@0 103
michael@0 104 clear();
michael@0 105 }
michael@0 106
michael@0 107
michael@0 108
michael@0 109 TDStretch::~TDStretch()
michael@0 110 {
michael@0 111 delete[] pMidBufferUnaligned;
michael@0 112 }
michael@0 113
michael@0 114
michael@0 115
michael@0 116 // Sets routine control parameters. These control are certain time constants
michael@0 117 // defining how the sound is stretched to the desired duration.
michael@0 118 //
michael@0 119 // 'sampleRate' = sample rate of the sound
michael@0 120 // 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
michael@0 121 // 'seekwindowMS' = seeking window length for scanning the best overlapping
michael@0 122 // position (default = 28 ms)
michael@0 123 // 'overlapMS' = overlapping length (default = 12 ms)
michael@0 124
michael@0 125 void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
michael@0 126 int aSeekWindowMS, int aOverlapMS)
michael@0 127 {
michael@0 128 // accept only positive parameter values - if zero or negative, use old values instead
michael@0 129 if (aSampleRate > 0) this->sampleRate = aSampleRate;
michael@0 130 if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
michael@0 131
michael@0 132 if (aSequenceMS > 0)
michael@0 133 {
michael@0 134 this->sequenceMs = aSequenceMS;
michael@0 135 bAutoSeqSetting = false;
michael@0 136 }
michael@0 137 else if (aSequenceMS == 0)
michael@0 138 {
michael@0 139 // if zero, use automatic setting
michael@0 140 bAutoSeqSetting = true;
michael@0 141 }
michael@0 142
michael@0 143 if (aSeekWindowMS > 0)
michael@0 144 {
michael@0 145 this->seekWindowMs = aSeekWindowMS;
michael@0 146 bAutoSeekSetting = false;
michael@0 147 }
michael@0 148 else if (aSeekWindowMS == 0)
michael@0 149 {
michael@0 150 // if zero, use automatic setting
michael@0 151 bAutoSeekSetting = true;
michael@0 152 }
michael@0 153
michael@0 154 calcSeqParameters();
michael@0 155
michael@0 156 calculateOverlapLength(overlapMs);
michael@0 157
michael@0 158 // set tempo to recalculate 'sampleReq'
michael@0 159 setTempo(tempo);
michael@0 160 }
michael@0 161
michael@0 162
michael@0 163
michael@0 164 /// Get routine control parameters, see setParameters() function.
michael@0 165 /// Any of the parameters to this function can be NULL, in such case corresponding parameter
michael@0 166 /// value isn't returned.
michael@0 167 void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
michael@0 168 {
michael@0 169 if (pSampleRate)
michael@0 170 {
michael@0 171 *pSampleRate = sampleRate;
michael@0 172 }
michael@0 173
michael@0 174 if (pSequenceMs)
michael@0 175 {
michael@0 176 *pSequenceMs = (bAutoSeqSetting) ? (USE_AUTO_SEQUENCE_LEN) : sequenceMs;
michael@0 177 }
michael@0 178
michael@0 179 if (pSeekWindowMs)
michael@0 180 {
michael@0 181 *pSeekWindowMs = (bAutoSeekSetting) ? (USE_AUTO_SEEKWINDOW_LEN) : seekWindowMs;
michael@0 182 }
michael@0 183
michael@0 184 if (pOverlapMs)
michael@0 185 {
michael@0 186 *pOverlapMs = overlapMs;
michael@0 187 }
michael@0 188 }
michael@0 189
michael@0 190
michael@0 191 // Overlaps samples in 'midBuffer' with the samples in 'pInput'
michael@0 192 void TDStretch::overlapMono(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput) const
michael@0 193 {
michael@0 194 int i;
michael@0 195 SAMPLETYPE m1, m2;
michael@0 196
michael@0 197 m1 = (SAMPLETYPE)0;
michael@0 198 m2 = (SAMPLETYPE)overlapLength;
michael@0 199
michael@0 200 for (i = 0; i < overlapLength ; i ++)
michael@0 201 {
michael@0 202 pOutput[i] = (pInput[i] * m1 + pMidBuffer[i] * m2 ) / overlapLength;
michael@0 203 m1 += 1;
michael@0 204 m2 -= 1;
michael@0 205 }
michael@0 206 }
michael@0 207
michael@0 208
michael@0 209
michael@0 210 void TDStretch::clearMidBuffer()
michael@0 211 {
michael@0 212 memset(pMidBuffer, 0, channels * sizeof(SAMPLETYPE) * overlapLength);
michael@0 213 }
michael@0 214
michael@0 215
michael@0 216 void TDStretch::clearInput()
michael@0 217 {
michael@0 218 inputBuffer.clear();
michael@0 219 clearMidBuffer();
michael@0 220 }
michael@0 221
michael@0 222
michael@0 223 // Clears the sample buffers
michael@0 224 void TDStretch::clear()
michael@0 225 {
michael@0 226 outputBuffer.clear();
michael@0 227 clearInput();
michael@0 228 }
michael@0 229
michael@0 230
michael@0 231
michael@0 232 // Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
michael@0 233 // to enable
michael@0 234 void TDStretch::enableQuickSeek(bool enable)
michael@0 235 {
michael@0 236 bQuickSeek = enable;
michael@0 237 }
michael@0 238
michael@0 239
michael@0 240 // Returns nonzero if the quick seeking algorithm is enabled.
michael@0 241 bool TDStretch::isQuickSeekEnabled() const
michael@0 242 {
michael@0 243 return bQuickSeek;
michael@0 244 }
michael@0 245
michael@0 246
michael@0 247 // Seeks for the optimal overlap-mixing position.
michael@0 248 int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
michael@0 249 {
michael@0 250 if (bQuickSeek)
michael@0 251 {
michael@0 252 return seekBestOverlapPositionQuick(refPos);
michael@0 253 }
michael@0 254 else
michael@0 255 {
michael@0 256 return seekBestOverlapPositionFull(refPos);
michael@0 257 }
michael@0 258 }
michael@0 259
michael@0 260
michael@0 261 // Overlaps samples in 'midBuffer' with the samples in 'pInputBuffer' at position
michael@0 262 // of 'ovlPos'.
michael@0 263 inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, uint ovlPos) const
michael@0 264 {
michael@0 265 #ifndef USE_MULTICH_ALWAYS
michael@0 266 if (channels == 1)
michael@0 267 {
michael@0 268 // mono sound.
michael@0 269 overlapMono(pOutput, pInput + ovlPos);
michael@0 270 }
michael@0 271 else if (channels == 2)
michael@0 272 {
michael@0 273 // stereo sound
michael@0 274 overlapStereo(pOutput, pInput + 2 * ovlPos);
michael@0 275 }
michael@0 276 else
michael@0 277 #endif // USE_MULTICH_ALWAYS
michael@0 278 {
michael@0 279 assert(channels > 0);
michael@0 280 overlapMulti(pOutput, pInput + channels * ovlPos);
michael@0 281 }
michael@0 282 }
michael@0 283
michael@0 284
michael@0 285
michael@0 286 // Seeks for the optimal overlap-mixing position. The 'stereo' version of the
michael@0 287 // routine
michael@0 288 //
michael@0 289 // The best position is determined as the position where the two overlapped
michael@0 290 // sample sequences are 'most alike', in terms of the highest cross-correlation
michael@0 291 // value over the overlapping period
michael@0 292 int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
michael@0 293 {
michael@0 294 int bestOffs;
michael@0 295 double bestCorr, corr;
michael@0 296 double norm;
michael@0 297 int i;
michael@0 298
michael@0 299 bestCorr = FLT_MIN;
michael@0 300 bestOffs = 0;
michael@0 301
michael@0 302 // Scans for the best correlation value by testing each possible position
michael@0 303 // over the permitted range.
michael@0 304 bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
michael@0 305 for (i = 1; i < seekLength; i ++)
michael@0 306 {
michael@0 307 // Calculates correlation value for the mixing position corresponding
michael@0 308 // to 'i'. Now call "calcCrossCorrAccumulate" that is otherwise same as
michael@0 309 // "calcCrossCorr", but saves time by reusing & updating previously stored
michael@0 310 // "norm" value
michael@0 311 corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
michael@0 312
michael@0 313 // heuristic rule to slightly favour values close to mid of the range
michael@0 314 double tmp = (double)(2 * i - seekLength) / (double)seekLength;
michael@0 315 corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
michael@0 316
michael@0 317 // Checks for the highest correlation value
michael@0 318 if (corr > bestCorr)
michael@0 319 {
michael@0 320 bestCorr = corr;
michael@0 321 bestOffs = i;
michael@0 322 }
michael@0 323 }
michael@0 324 // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
michael@0 325 clearCrossCorrState();
michael@0 326
michael@0 327 return bestOffs;
michael@0 328 }
michael@0 329
michael@0 330
michael@0 331 // Seeks for the optimal overlap-mixing position. The 'stereo' version of the
michael@0 332 // routine
michael@0 333 //
michael@0 334 // The best position is determined as the position where the two overlapped
michael@0 335 // sample sequences are 'most alike', in terms of the highest cross-correlation
michael@0 336 // value over the overlapping period
michael@0 337 int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
michael@0 338 {
michael@0 339 int j;
michael@0 340 int bestOffs;
michael@0 341 double bestCorr, corr;
michael@0 342 int scanCount, corrOffset, tempOffset;
michael@0 343
michael@0 344 bestCorr = FLT_MIN;
michael@0 345 bestOffs = _scanOffsets[0][0];
michael@0 346 corrOffset = 0;
michael@0 347 tempOffset = 0;
michael@0 348
michael@0 349 // Scans for the best correlation value using four-pass hierarchical search.
michael@0 350 //
michael@0 351 // The look-up table 'scans' has hierarchical position adjusting steps.
michael@0 352 // In first pass the routine searhes for the highest correlation with
michael@0 353 // relatively coarse steps, then rescans the neighbourhood of the highest
michael@0 354 // correlation with better resolution and so on.
michael@0 355 for (scanCount = 0;scanCount < 4; scanCount ++)
michael@0 356 {
michael@0 357 j = 0;
michael@0 358 while (_scanOffsets[scanCount][j])
michael@0 359 {
michael@0 360 double norm;
michael@0 361 tempOffset = corrOffset + _scanOffsets[scanCount][j];
michael@0 362 if (tempOffset >= seekLength) break;
michael@0 363
michael@0 364 // Calculates correlation value for the mixing position corresponding
michael@0 365 // to 'tempOffset'
michael@0 366 corr = (double)calcCrossCorr(refPos + channels * tempOffset, pMidBuffer, norm);
michael@0 367 // heuristic rule to slightly favour values close to mid of the range
michael@0 368 double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
michael@0 369 corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
michael@0 370
michael@0 371 // Checks for the highest correlation value
michael@0 372 if (corr > bestCorr)
michael@0 373 {
michael@0 374 bestCorr = corr;
michael@0 375 bestOffs = tempOffset;
michael@0 376 }
michael@0 377 j ++;
michael@0 378 }
michael@0 379 corrOffset = bestOffs;
michael@0 380 }
michael@0 381 // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
michael@0 382 clearCrossCorrState();
michael@0 383
michael@0 384 return bestOffs;
michael@0 385 }
michael@0 386
michael@0 387
michael@0 388
michael@0 389 /// clear cross correlation routine state if necessary
michael@0 390 void TDStretch::clearCrossCorrState()
michael@0 391 {
michael@0 392 // default implementation is empty.
michael@0 393 }
michael@0 394
michael@0 395
michael@0 396 /// Calculates processing sequence length according to tempo setting
michael@0 397 void TDStretch::calcSeqParameters()
michael@0 398 {
michael@0 399 // Adjust tempo param according to tempo, so that variating processing sequence length is used
michael@0 400 // at varius tempo settings, between the given low...top limits
michael@0 401 #define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
michael@0 402 #define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
michael@0 403
michael@0 404 // sequence-ms setting values at above low & top tempo
michael@0 405 #define AUTOSEQ_AT_MIN 125.0
michael@0 406 #define AUTOSEQ_AT_MAX 50.0
michael@0 407 #define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
michael@0 408 #define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
michael@0 409
michael@0 410 // seek-window-ms setting values at above low & top tempo
michael@0 411 #define AUTOSEEK_AT_MIN 25.0
michael@0 412 #define AUTOSEEK_AT_MAX 15.0
michael@0 413 #define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
michael@0 414 #define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
michael@0 415
michael@0 416 #define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
michael@0 417
michael@0 418 double seq, seek;
michael@0 419
michael@0 420 if (bAutoSeqSetting)
michael@0 421 {
michael@0 422 seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
michael@0 423 seq = CHECK_LIMITS(seq, AUTOSEQ_AT_MAX, AUTOSEQ_AT_MIN);
michael@0 424 sequenceMs = (int)(seq + 0.5);
michael@0 425 }
michael@0 426
michael@0 427 if (bAutoSeekSetting)
michael@0 428 {
michael@0 429 seek = AUTOSEEK_C + AUTOSEEK_K * tempo;
michael@0 430 seek = CHECK_LIMITS(seek, AUTOSEEK_AT_MAX, AUTOSEEK_AT_MIN);
michael@0 431 seekWindowMs = (int)(seek + 0.5);
michael@0 432 }
michael@0 433
michael@0 434 // Update seek window lengths
michael@0 435 seekWindowLength = (sampleRate * sequenceMs) / 1000;
michael@0 436 if (seekWindowLength < 2 * overlapLength)
michael@0 437 {
michael@0 438 seekWindowLength = 2 * overlapLength;
michael@0 439 }
michael@0 440 seekLength = (sampleRate * seekWindowMs) / 1000;
michael@0 441 }
michael@0 442
michael@0 443
michael@0 444
michael@0 445 // Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
michael@0 446 // tempo, larger faster tempo.
michael@0 447 void TDStretch::setTempo(float newTempo)
michael@0 448 {
michael@0 449 int intskip;
michael@0 450
michael@0 451 tempo = newTempo;
michael@0 452
michael@0 453 // Calculate new sequence duration
michael@0 454 calcSeqParameters();
michael@0 455
michael@0 456 // Calculate ideal skip length (according to tempo value)
michael@0 457 nominalSkip = tempo * (seekWindowLength - overlapLength);
michael@0 458 intskip = (int)(nominalSkip + 0.5f);
michael@0 459
michael@0 460 // Calculate how many samples are needed in the 'inputBuffer' to
michael@0 461 // process another batch of samples
michael@0 462 //sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
michael@0 463 sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
michael@0 464 }
michael@0 465
michael@0 466
michael@0 467
michael@0 468 // Sets the number of channels, 1 = mono, 2 = stereo
michael@0 469 void TDStretch::setChannels(int numChannels)
michael@0 470 {
michael@0 471 assert(numChannels > 0);
michael@0 472 if (channels == numChannels) return;
michael@0 473 // assert(numChannels == 1 || numChannels == 2);
michael@0 474
michael@0 475 channels = numChannels;
michael@0 476 inputBuffer.setChannels(channels);
michael@0 477 outputBuffer.setChannels(channels);
michael@0 478
michael@0 479 // re-init overlap/buffer
michael@0 480 overlapLength=0;
michael@0 481 setParameters(sampleRate);
michael@0 482 }
michael@0 483
michael@0 484
michael@0 485 // nominal tempo, no need for processing, just pass the samples through
michael@0 486 // to outputBuffer
michael@0 487 /*
michael@0 488 void TDStretch::processNominalTempo()
michael@0 489 {
michael@0 490 assert(tempo == 1.0f);
michael@0 491
michael@0 492 if (bMidBufferDirty)
michael@0 493 {
michael@0 494 // If there are samples in pMidBuffer waiting for overlapping,
michael@0 495 // do a single sliding overlapping with them in order to prevent a
michael@0 496 // clicking distortion in the output sound
michael@0 497 if (inputBuffer.numSamples() < overlapLength)
michael@0 498 {
michael@0 499 // wait until we've got overlapLength input samples
michael@0 500 return;
michael@0 501 }
michael@0 502 // Mix the samples in the beginning of 'inputBuffer' with the
michael@0 503 // samples in 'midBuffer' using sliding overlapping
michael@0 504 overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
michael@0 505 outputBuffer.putSamples(overlapLength);
michael@0 506 inputBuffer.receiveSamples(overlapLength);
michael@0 507 clearMidBuffer();
michael@0 508 // now we've caught the nominal sample flow and may switch to
michael@0 509 // bypass mode
michael@0 510 }
michael@0 511
michael@0 512 // Simply bypass samples from input to output
michael@0 513 outputBuffer.moveSamples(inputBuffer);
michael@0 514 }
michael@0 515 */
michael@0 516
michael@0 517
michael@0 518 // Processes as many processing frames of the samples 'inputBuffer', store
michael@0 519 // the result into 'outputBuffer'
michael@0 520 void TDStretch::processSamples()
michael@0 521 {
michael@0 522 int ovlSkip, offset;
michael@0 523 int temp;
michael@0 524
michael@0 525 /* Removed this small optimization - can introduce a click to sound when tempo setting
michael@0 526 crosses the nominal value
michael@0 527 if (tempo == 1.0f)
michael@0 528 {
michael@0 529 // tempo not changed from the original, so bypass the processing
michael@0 530 processNominalTempo();
michael@0 531 return;
michael@0 532 }
michael@0 533 */
michael@0 534
michael@0 535 // Process samples as long as there are enough samples in 'inputBuffer'
michael@0 536 // to form a processing frame.
michael@0 537 while ((int)inputBuffer.numSamples() >= sampleReq)
michael@0 538 {
michael@0 539 // If tempo differs from the normal ('SCALE'), scan for the best overlapping
michael@0 540 // position
michael@0 541 offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
michael@0 542
michael@0 543 // Mix the samples in the 'inputBuffer' at position of 'offset' with the
michael@0 544 // samples in 'midBuffer' using sliding overlapping
michael@0 545 // ... first partially overlap with the end of the previous sequence
michael@0 546 // (that's in 'midBuffer')
michael@0 547 overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
michael@0 548 outputBuffer.putSamples((uint)overlapLength);
michael@0 549
michael@0 550 // ... then copy sequence samples from 'inputBuffer' to output:
michael@0 551
michael@0 552 // length of sequence
michael@0 553 temp = (seekWindowLength - 2 * overlapLength);
michael@0 554
michael@0 555 // crosscheck that we don't have buffer overflow...
michael@0 556 if ((int)inputBuffer.numSamples() < (offset + temp + overlapLength * 2))
michael@0 557 {
michael@0 558 continue; // just in case, shouldn't really happen
michael@0 559 }
michael@0 560
michael@0 561 outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp);
michael@0 562
michael@0 563 // Copies the end of the current sequence from 'inputBuffer' to
michael@0 564 // 'midBuffer' for being mixed with the beginning of the next
michael@0 565 // processing sequence and so on
michael@0 566 assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples());
michael@0 567 memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
michael@0 568 channels * sizeof(SAMPLETYPE) * overlapLength);
michael@0 569
michael@0 570 // Remove the processed samples from the input buffer. Update
michael@0 571 // the difference between integer & nominal skip step to 'skipFract'
michael@0 572 // in order to prevent the error from accumulating over time.
michael@0 573 skipFract += nominalSkip; // real skip size
michael@0 574 ovlSkip = (int)skipFract; // rounded to integer skip
michael@0 575 skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip
michael@0 576 inputBuffer.receiveSamples((uint)ovlSkip);
michael@0 577 }
michael@0 578 }
michael@0 579
michael@0 580
michael@0 581 // Adds 'numsamples' pcs of samples from the 'samples' memory position into
michael@0 582 // the input of the object.
michael@0 583 void TDStretch::putSamples(const SAMPLETYPE *samples, uint nSamples)
michael@0 584 {
michael@0 585 // Add the samples into the input buffer
michael@0 586 inputBuffer.putSamples(samples, nSamples);
michael@0 587 // Process the samples in input buffer
michael@0 588 processSamples();
michael@0 589 }
michael@0 590
michael@0 591
michael@0 592
michael@0 593 /// Set new overlap length parameter & reallocate RefMidBuffer if necessary.
michael@0 594 void TDStretch::acceptNewOverlapLength(int newOverlapLength)
michael@0 595 {
michael@0 596 int prevOvl;
michael@0 597
michael@0 598 assert(newOverlapLength >= 0);
michael@0 599 prevOvl = overlapLength;
michael@0 600 overlapLength = newOverlapLength;
michael@0 601
michael@0 602 if (overlapLength > prevOvl)
michael@0 603 {
michael@0 604 delete[] pMidBufferUnaligned;
michael@0 605
michael@0 606 pMidBufferUnaligned = new SAMPLETYPE[overlapLength * channels + 16 / sizeof(SAMPLETYPE)];
michael@0 607 // ensure that 'pMidBuffer' is aligned to 16 byte boundary for efficiency
michael@0 608 pMidBuffer = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(pMidBufferUnaligned);
michael@0 609
michael@0 610 clearMidBuffer();
michael@0 611 }
michael@0 612 }
michael@0 613
michael@0 614
michael@0 615 // Operator 'new' is overloaded so that it automatically creates a suitable instance
michael@0 616 // depending on if we've a MMX/SSE/etc-capable CPU available or not.
michael@0 617 void * TDStretch::operator new(size_t s)
michael@0 618 {
michael@0 619 // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
michael@0 620 ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
michael@0 621 return newInstance();
michael@0 622 }
michael@0 623
michael@0 624
michael@0 625 TDStretch * TDStretch::newInstance()
michael@0 626 {
michael@0 627 #if defined(SOUNDTOUCH_ALLOW_MMX) || defined(SOUNDTOUCH_ALLOW_SSE)
michael@0 628 uint uExtensions;
michael@0 629
michael@0 630 uExtensions = detectCPUextensions();
michael@0 631 #endif
michael@0 632
michael@0 633 // Check if MMX/SSE instruction set extensions supported by CPU
michael@0 634
michael@0 635 #ifdef SOUNDTOUCH_ALLOW_MMX
michael@0 636 // MMX routines available only with integer sample types
michael@0 637 if (uExtensions & SUPPORT_MMX)
michael@0 638 {
michael@0 639 return ::new TDStretchMMX;
michael@0 640 }
michael@0 641 else
michael@0 642 #endif // SOUNDTOUCH_ALLOW_MMX
michael@0 643
michael@0 644
michael@0 645 #ifdef SOUNDTOUCH_ALLOW_SSE
michael@0 646 if (uExtensions & SUPPORT_SSE)
michael@0 647 {
michael@0 648 // SSE support
michael@0 649 return ::new TDStretchSSE;
michael@0 650 }
michael@0 651 else
michael@0 652 #endif // SOUNDTOUCH_ALLOW_SSE
michael@0 653
michael@0 654 {
michael@0 655 // ISA optimizations not supported, use plain C version
michael@0 656 return ::new TDStretch;
michael@0 657 }
michael@0 658 }
michael@0 659
michael@0 660
michael@0 661 //////////////////////////////////////////////////////////////////////////////
michael@0 662 //
michael@0 663 // Integer arithmetics specific algorithm implementations.
michael@0 664 //
michael@0 665 //////////////////////////////////////////////////////////////////////////////
michael@0 666
michael@0 667 #ifdef SOUNDTOUCH_INTEGER_SAMPLES
michael@0 668
michael@0 669 // Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
michael@0 670 // version of the routine.
michael@0 671 void TDStretch::overlapStereo(short *poutput, const short *input) const
michael@0 672 {
michael@0 673 int i;
michael@0 674 short temp;
michael@0 675 int cnt2;
michael@0 676
michael@0 677 for (i = 0; i < overlapLength ; i ++)
michael@0 678 {
michael@0 679 temp = (short)(overlapLength - i);
michael@0 680 cnt2 = 2 * i;
michael@0 681 poutput[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
michael@0 682 poutput[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
michael@0 683 }
michael@0 684 }
michael@0 685
michael@0 686
michael@0 687 // Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Multi'
michael@0 688 // version of the routine.
michael@0 689 void TDStretch::overlapMulti(SAMPLETYPE *poutput, const SAMPLETYPE *input) const
michael@0 690 {
michael@0 691 SAMPLETYPE m1=(SAMPLETYPE)0;
michael@0 692 SAMPLETYPE m2;
michael@0 693 int i=0;
michael@0 694
michael@0 695 for (m2 = (SAMPLETYPE)overlapLength; m2; m2 --)
michael@0 696 {
michael@0 697 for (int c = 0; c < channels; c ++)
michael@0 698 {
michael@0 699 poutput[i] = (input[i] * m1 + pMidBuffer[i] * m2) / overlapLength;
michael@0 700 i++;
michael@0 701 }
michael@0 702
michael@0 703 m1++;
michael@0 704 }
michael@0 705 }
michael@0 706
michael@0 707 // Calculates the x having the closest 2^x value for the given value
michael@0 708 static int _getClosest2Power(double value)
michael@0 709 {
michael@0 710 return (int)(log(value) / log(2.0) + 0.5);
michael@0 711 }
michael@0 712
michael@0 713
michael@0 714 /// Calculates overlap period length in samples.
michael@0 715 /// Integer version rounds overlap length to closest power of 2
michael@0 716 /// for a divide scaling operation.
michael@0 717 void TDStretch::calculateOverlapLength(int aoverlapMs)
michael@0 718 {
michael@0 719 int newOvl;
michael@0 720
michael@0 721 assert(aoverlapMs >= 0);
michael@0 722
michael@0 723 // calculate overlap length so that it's power of 2 - thus it's easy to do
michael@0 724 // integer division by right-shifting. Term "-1" at end is to account for
michael@0 725 // the extra most significatnt bit left unused in result by signed multiplication
michael@0 726 overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
michael@0 727 if (overlapDividerBits > 9) overlapDividerBits = 9;
michael@0 728 if (overlapDividerBits < 3) overlapDividerBits = 3;
michael@0 729 newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
michael@0 730
michael@0 731 acceptNewOverlapLength(newOvl);
michael@0 732
michael@0 733 // calculate sloping divider so that crosscorrelation operation won't
michael@0 734 // overflow 32-bit register. Max. sum of the crosscorrelation sum without
michael@0 735 // divider would be 2^30*(N^3-N)/3, where N = overlap length
michael@0 736 slopingDivider = (newOvl * newOvl - 1) / 3;
michael@0 737 }
michael@0 738
michael@0 739
michael@0 740 double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const
michael@0 741 {
michael@0 742 long corr;
michael@0 743 long lnorm;
michael@0 744 int i;
michael@0 745
michael@0 746 corr = lnorm = 0;
michael@0 747 // Same routine for stereo and mono. For stereo, unroll loop for better
michael@0 748 // efficiency and gives slightly better resolution against rounding.
michael@0 749 // For mono it same routine, just unrolls loop by factor of 4
michael@0 750 for (i = 0; i < channels * overlapLength; i += 4)
michael@0 751 {
michael@0 752 corr += (mixingPos[i] * compare[i] +
michael@0 753 mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
michael@0 754 corr += (mixingPos[i + 2] * compare[i + 2] +
michael@0 755 mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
michael@0 756 lnorm += (mixingPos[i] * mixingPos[i] +
michael@0 757 mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
michael@0 758 lnorm += (mixingPos[i + 2] * mixingPos[i + 2] +
michael@0 759 mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBits;
michael@0 760 }
michael@0 761
michael@0 762 // Normalize result by dividing by sqrt(norm) - this step is easiest
michael@0 763 // done using floating point operation
michael@0 764 norm = (double)lnorm;
michael@0 765 return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
michael@0 766 }
michael@0 767
michael@0 768
michael@0 769 /// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
michael@0 770 double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const
michael@0 771 {
michael@0 772 long corr;
michael@0 773 long lnorm;
michael@0 774 int i;
michael@0 775
michael@0 776 // cancel first normalizer tap from previous round
michael@0 777 lnorm = 0;
michael@0 778 for (i = 1; i <= channels; i ++)
michael@0 779 {
michael@0 780 lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBits;
michael@0 781 }
michael@0 782
michael@0 783 corr = 0;
michael@0 784 // Same routine for stereo and mono. For stereo, unroll loop for better
michael@0 785 // efficiency and gives slightly better resolution against rounding.
michael@0 786 // For mono it same routine, just unrolls loop by factor of 4
michael@0 787 for (i = 0; i < channels * overlapLength; i += 4)
michael@0 788 {
michael@0 789 corr += (mixingPos[i] * compare[i] +
michael@0 790 mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
michael@0 791 corr += (mixingPos[i + 2] * compare[i + 2] +
michael@0 792 mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
michael@0 793 }
michael@0 794
michael@0 795 // update normalizer with last samples of this round
michael@0 796 for (int j = 0; j < channels; j ++)
michael@0 797 {
michael@0 798 i --;
michael@0 799 lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
michael@0 800 }
michael@0 801 norm += (double)lnorm;
michael@0 802
michael@0 803 // Normalize result by dividing by sqrt(norm) - this step is easiest
michael@0 804 // done using floating point operation
michael@0 805 return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
michael@0 806 }
michael@0 807
michael@0 808 #endif // SOUNDTOUCH_INTEGER_SAMPLES
michael@0 809
michael@0 810 //////////////////////////////////////////////////////////////////////////////
michael@0 811 //
michael@0 812 // Floating point arithmetics specific algorithm implementations.
michael@0 813 //
michael@0 814
michael@0 815 #ifdef SOUNDTOUCH_FLOAT_SAMPLES
michael@0 816
michael@0 817 // Overlaps samples in 'midBuffer' with the samples in 'pInput'
michael@0 818 void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
michael@0 819 {
michael@0 820 int i;
michael@0 821 float fScale;
michael@0 822 float f1;
michael@0 823 float f2;
michael@0 824
michael@0 825 fScale = 1.0f / (float)overlapLength;
michael@0 826
michael@0 827 f1 = 0;
michael@0 828 f2 = 1.0f;
michael@0 829
michael@0 830 for (i = 0; i < 2 * (int)overlapLength ; i += 2)
michael@0 831 {
michael@0 832 pOutput[i + 0] = pInput[i + 0] * f1 + pMidBuffer[i + 0] * f2;
michael@0 833 pOutput[i + 1] = pInput[i + 1] * f1 + pMidBuffer[i + 1] * f2;
michael@0 834
michael@0 835 f1 += fScale;
michael@0 836 f2 -= fScale;
michael@0 837 }
michael@0 838 }
michael@0 839
michael@0 840
michael@0 841 // Overlaps samples in 'midBuffer' with the samples in 'input'.
michael@0 842 void TDStretch::overlapMulti(float *pOutput, const float *pInput) const
michael@0 843 {
michael@0 844 int i;
michael@0 845 float fScale;
michael@0 846 float f1;
michael@0 847 float f2;
michael@0 848
michael@0 849 fScale = 1.0f / (float)overlapLength;
michael@0 850
michael@0 851 f1 = 0;
michael@0 852 f2 = 1.0f;
michael@0 853
michael@0 854 i=0;
michael@0 855 for (int i2 = 0; i2 < overlapLength; i2 ++)
michael@0 856 {
michael@0 857 // note: Could optimize this slightly by taking into account that always channels > 2
michael@0 858 for (int c = 0; c < channels; c ++)
michael@0 859 {
michael@0 860 pOutput[i] = pInput[i] * f1 + pMidBuffer[i] * f2;
michael@0 861 i++;
michael@0 862 }
michael@0 863 f1 += fScale;
michael@0 864 f2 -= fScale;
michael@0 865 }
michael@0 866 }
michael@0 867
michael@0 868
michael@0 869 /// Calculates overlapInMsec period length in samples.
michael@0 870 void TDStretch::calculateOverlapLength(int overlapInMsec)
michael@0 871 {
michael@0 872 int newOvl;
michael@0 873
michael@0 874 assert(overlapInMsec >= 0);
michael@0 875 newOvl = (sampleRate * overlapInMsec) / 1000;
michael@0 876 if (newOvl < 16) newOvl = 16;
michael@0 877
michael@0 878 // must be divisible by 8
michael@0 879 newOvl -= newOvl % 8;
michael@0 880
michael@0 881 acceptNewOverlapLength(newOvl);
michael@0 882 }
michael@0 883
michael@0 884
michael@0 885 /// Calculate cross-correlation
michael@0 886 double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const
michael@0 887 {
michael@0 888 double corr;
michael@0 889 int i;
michael@0 890
michael@0 891 corr = norm = 0;
michael@0 892 // Same routine for stereo and mono. For Stereo, unroll by factor of 2.
michael@0 893 // For mono it's same routine yet unrollsd by factor of 4.
michael@0 894 for (i = 0; i < channels * overlapLength; i += 4)
michael@0 895 {
michael@0 896 corr += mixingPos[i] * compare[i] +
michael@0 897 mixingPos[i + 1] * compare[i + 1];
michael@0 898
michael@0 899 norm += mixingPos[i] * mixingPos[i] +
michael@0 900 mixingPos[i + 1] * mixingPos[i + 1];
michael@0 901
michael@0 902 // unroll the loop for better CPU efficiency:
michael@0 903 corr += mixingPos[i + 2] * compare[i + 2] +
michael@0 904 mixingPos[i + 3] * compare[i + 3];
michael@0 905
michael@0 906 norm += mixingPos[i + 2] * mixingPos[i + 2] +
michael@0 907 mixingPos[i + 3] * mixingPos[i + 3];
michael@0 908 }
michael@0 909
michael@0 910 return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
michael@0 911 }
michael@0 912
michael@0 913
michael@0 914 /// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
michael@0 915 double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const
michael@0 916 {
michael@0 917 double corr;
michael@0 918 int i;
michael@0 919
michael@0 920 corr = 0;
michael@0 921
michael@0 922 // cancel first normalizer tap from previous round
michael@0 923 for (i = 1; i <= channels; i ++)
michael@0 924 {
michael@0 925 norm -= mixingPos[-i] * mixingPos[-i];
michael@0 926 }
michael@0 927
michael@0 928 // Same routine for stereo and mono. For Stereo, unroll by factor of 2.
michael@0 929 // For mono it's same routine yet unrollsd by factor of 4.
michael@0 930 for (i = 0; i < channels * overlapLength; i += 4)
michael@0 931 {
michael@0 932 corr += mixingPos[i] * compare[i] +
michael@0 933 mixingPos[i + 1] * compare[i + 1] +
michael@0 934 mixingPos[i + 2] * compare[i + 2] +
michael@0 935 mixingPos[i + 3] * compare[i + 3];
michael@0 936 }
michael@0 937
michael@0 938 // update normalizer with last samples of this round
michael@0 939 for (int j = 0; j < channels; j ++)
michael@0 940 {
michael@0 941 i --;
michael@0 942 norm += mixingPos[i] * mixingPos[i];
michael@0 943 }
michael@0 944
michael@0 945 return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
michael@0 946 }
michael@0 947
michael@0 948
michael@0 949 #endif // SOUNDTOUCH_FLOAT_SAMPLES

mercurial