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