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