Thu, 22 Jan 2015 13:21:57 +0100
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 |