📄 tdstretch.cpp
字号:
bestCorr = corr; bestOffs = tempOffset; } j ++; } corrOffset = bestOffs; } // clear cross correlation routine state if necessary (is so e.g. in MMX routines). clearCrossCorrState(); return bestOffs;}/// clear cross correlation routine state if necessary void TDStretch::clearCrossCorrState(){ // default implementation is empty.}// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower // tempo, larger faster tempo.void TDStretch::setTempo(float newTempo){ uint intskip; tempo = newTempo; // Calculate ideal skip length (according to tempo value) nominalSkip = tempo * (seekWindowLength - overlapLength); skipFract = 0; intskip = (int)(nominalSkip + 0.5f); // Calculate how many samples are needed in the 'inputBuffer' to // process another batch of samples sampleReq = max(intskip + overlapLength, seekWindowLength) + maxOffset;}// Sets the number of channels, 1 = mono, 2 = stereovoid TDStretch::setChannels(uint numChannels){ if (channels == numChannels) return; assert(numChannels == 1 || numChannels == 2); channels = numChannels; inputBuffer.setChannels(channels); outputBuffer.setChannels(channels);}// nominal tempo, no need for processing, just pass the samples through// to outputBuffervoid TDStretch::processNominalTempo(){ assert(tempo == 1.0f); if (bMidBufferDirty) { // If there are samples in pMidBuffer waiting for overlapping, // do a single sliding overlapping with them in order to prevent a // clicking distortion in the output sound if (inputBuffer.numSamples() < overlapLength) { // wait until we've got overlapLength input samples return; } // Mix the samples in the beginning of 'inputBuffer' with the // samples in 'midBuffer' using sliding overlapping overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0); outputBuffer.putSamples(overlapLength); inputBuffer.receiveSamples(overlapLength); clearMidBuffer(); // now we've caught the nominal sample flow and may switch to // bypass mode } // Simply bypass samples from input to output outputBuffer.moveSamples(inputBuffer);}// Processes as many processing frames of the samples 'inputBuffer', store// the result into 'outputBuffer'void TDStretch::processSamples(){ uint ovlSkip, offset; int temp; if (tempo == 1.0f) { // tempo not changed from the original, so bypass the processing processNominalTempo(); return; } if (bMidBufferDirty == FALSE) { // if midBuffer is empty, move the first samples of the input stream // into it if (inputBuffer.numSamples() < overlapLength) { // wait until we've got overlapLength samples return; } memcpy(pMidBuffer, inputBuffer.ptrBegin(), channels * overlapLength * sizeof(SAMPLETYPE)); inputBuffer.receiveSamples(overlapLength); bMidBufferDirty = TRUE; } // Process samples as long as there are enough samples in 'inputBuffer' // to form a processing frame. while (inputBuffer.numSamples() >= sampleReq) { // If tempo differs from the normal ('SCALE'), scan for the best overlapping // position offset = seekBestOverlapPosition(inputBuffer.ptrBegin()); // Mix the samples in the 'inputBuffer' at position of 'offset' with the // samples in 'midBuffer' using sliding overlapping // ... first partially overlap with the end of the previous sequence // (that's in 'midBuffer') overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), offset); outputBuffer.putSamples(overlapLength); // ... then copy sequence samples from 'inputBuffer' to output temp = (seekWindowLength - 2 * overlapLength);// & 0xfffffffe; if (temp > 0) { outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), temp); } // Copies the end of the current sequence from 'inputBuffer' to // 'midBuffer' for being mixed with the beginning of the next // processing sequence and so on assert(offset + seekWindowLength <= inputBuffer.numSamples()); memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + seekWindowLength - overlapLength), channels * sizeof(SAMPLETYPE) * overlapLength); bMidBufferDirty = TRUE; // Remove the processed samples from the input buffer. Update // the difference between integer & nominal skip step to 'skipFract' // in order to prevent the error from accumulating over time. skipFract += nominalSkip; // real skip size ovlSkip = (int)skipFract; // rounded to integer skip skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip inputBuffer.receiveSamples(ovlSkip); }}// Adds 'numsamples' pcs of samples from the 'samples' memory position into// the input of the object.void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples){ // Add the samples into the input buffer inputBuffer.putSamples(samples, numSamples); // Process the samples in input buffer processSamples();}/// Set new overlap length parameter & reallocate RefMidBuffer if necessary.void TDStretch::acceptNewOverlapLength(uint newOverlapLength){ uint prevOvl; prevOvl = overlapLength; overlapLength = newOverlapLength; if (overlapLength > prevOvl) { delete[] pMidBuffer; delete[] pRefMidBufferUnaligned; pMidBuffer = new SAMPLETYPE[overlapLength * 2]; bMidBufferDirty = TRUE; clearMidBuffer(); pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)]; // ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency pRefMidBuffer = (SAMPLETYPE *)((((ulong)pRefMidBufferUnaligned) + 15) & -16); }}// Operator 'new' is overloaded so that it automatically creates a suitable instance // depending on if we've a MMX/SSE/etc-capable CPU available or not.void * TDStretch::operator new(size_t s){ // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead! assert(FALSE); return NULL;}TDStretch * TDStretch::newInstance(){ uint uExtensions; uExtensions = detectCPUextensions(); // Check if MMX/SSE/3DNow! instruction set extensions supported by CPU#ifdef ALLOW_MMX // MMX routines available only with integer sample types if (uExtensions & SUPPORT_MMX) { return ::new TDStretchMMX; } else#endif // ALLOW_MMX#ifdef ALLOW_SSE if (uExtensions & SUPPORT_SSE) { // SSE support return ::new TDStretchSSE; } else#endif // ALLOW_SSE#ifdef ALLOW_3DNOW if (uExtensions & SUPPORT_3DNOW) { // 3DNow! support return ::new TDStretch3DNow; } else#endif // ALLOW_3DNOW { // ISA optimizations not supported, use plain C version return ::new TDStretch; }}////////////////////////////////////////////////////////////////////////////////// Integer arithmetics specific algorithm implementations.////////////////////////////////////////////////////////////////////////////////#ifdef INTEGER_SAMPLES// Slopes the amplitude of the 'midBuffer' samples so that cross correlation// is faster to calculatevoid TDStretch::precalcCorrReferenceStereo(){ int i, cnt2; int temp, temp2; for (i=0 ; i < (int)overlapLength ;i ++) { temp = i * (overlapLength - i); cnt2 = i * 2; temp2 = (pMidBuffer[cnt2] * temp) / slopingDivider; pRefMidBuffer[cnt2] = (short)(temp2); temp2 = (pMidBuffer[cnt2 + 1] * temp) / slopingDivider; pRefMidBuffer[cnt2 + 1] = (short)(temp2); }}// Slopes the amplitude of the 'midBuffer' samples so that cross correlation// is faster to calculatevoid TDStretch::precalcCorrReferenceMono(){ int i; long temp; long temp2; for (i=0 ; i < (int)overlapLength ;i ++) { temp = i * (overlapLength - i); temp2 = (pMidBuffer[i] * temp) / slopingDivider; pRefMidBuffer[i] = (short)temp2; }}// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo' // version of the routine.void TDStretch::overlapStereo(short *output, const short *input) const{ int i; short temp; uint cnt2; for (i = 0; i < (int)overlapLength ; i ++) { temp = (short)(overlapLength - i); cnt2 = 2 * i; output[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength; output[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength; }}/// Calculates overlap period length in samples./// Integer version rounds overlap length to closest power of 2/// for a divide scaling operation.void TDStretch::calculateOverlapLength(uint overlapMs){ uint newOvl; overlapDividerBits = _getClosest2Power((sampleRate * overlapMs) / 1000.0); if (overlapDividerBits > 9) overlapDividerBits = 9; if (overlapDividerBits < 4) overlapDividerBits = 4; newOvl = (uint)pow(2, overlapDividerBits); acceptNewOverlapLength(newOvl); // calculate sloping divider so that crosscorrelation operation won't // overflow 32-bit register. Max. sum of the crosscorrelation sum without // divider would be 2^30*(N^3-N)/3, where N = overlap length slopingDivider = (newOvl * newOvl - 1) / 3;}long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const{ long corr; uint i; corr = 0; for (i = 1; i < overlapLength; i ++) { corr += (mixingPos[i] * compare[i]) >> overlapDividerBits; } return corr;}long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const{ long corr; uint i; corr = 0; for (i = 2; i < 2 * overlapLength; i += 2) { corr += (mixingPos[i] * compare[i] + mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; } return corr;}#endif // INTEGER_SAMPLES////////////////////////////////////////////////////////////////////////////////// Floating point arithmetics specific algorithm implementations.//#ifdef FLOAT_SAMPLES// Slopes the amplitude of the 'midBuffer' samples so that cross correlation// is faster to calculatevoid TDStretch::precalcCorrReferenceStereo(){ int i, cnt2; float temp; for (i=0 ; i < (int)overlapLength ;i ++) { temp = (float)i * (float)(overlapLength - i); cnt2 = i * 2; pRefMidBuffer[cnt2] = (float)(pMidBuffer[cnt2] * temp); pRefMidBuffer[cnt2 + 1] = (float)(pMidBuffer[cnt2 + 1] * temp); }}// Slopes the amplitude of the 'midBuffer' samples so that cross correlation// is faster to calculatevoid TDStretch::precalcCorrReferenceMono(){ int i; float temp; for (i=0 ; i < (int)overlapLength ;i ++) { temp = (float)i * (float)(overlapLength - i); pRefMidBuffer[i] = (float)(pMidBuffer[i] * temp); }}// SSE-optimized version of the function overlapStereovoid TDStretch::overlapStereo(float *output, const float *input) const{ int i; uint cnt2; float fTemp; float fScale; float fi; fScale = 1.0f / (float)overlapLength; for (i = 0; i < (int)overlapLength ; i ++) { fTemp = (float)(overlapLength - i) * fScale; fi = (float)i * fScale; cnt2 = 2 * i; output[cnt2 + 0] = input[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp; output[cnt2 + 1] = input[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp; }}/// Calculates overlap period length in samples.void TDStretch::calculateOverlapLength(uint overlapMs){ uint newOvl; newOvl = (sampleRate * overlapMs) / 1000; if (newOvl < 16) newOvl = 16; acceptNewOverlapLength(newOvl);}double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const{ double corr; uint i; corr = 0; for (i = 1; i < overlapLength; i ++) { corr += mixingPos[i] * compare[i]; } return corr;}double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const{ double corr; uint i; corr = 0; for (i = 2; i < 2 * overlapLength; i += 2) { corr += mixingPos[i] * compare[i] + mixingPos[i + 1] * compare[i + 1]; } return corr;}#endif // FLOAT_SAMPLES
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -