📄 tdstretch.cpp
字号:
/***************************************************************************** * * Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo * while maintaining the original pitch by using a time domain WSOLA-like * method with several performance-increasing tweaks. * * Note : MMX optimized functions reside in a separate, platform-specific * file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp' * * Author : Copyright (c) Olli Parviainen * Author e-mail : oparviai @ iki.fi * File created : 13-Jan-2002 * * Last changed : $Date: 2004/10/26 19:09:37 $ * File revision : $Revision: 1.4 $ * * $Id: TDStretch.cpp,v 1.4 2004/10/26 19:09:37 vjohnson Exp $ * * License : * * SoundTouch sound processing library * Copyright (c) Olli Parviainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *****************************************************************************/#include <string.h>#include <stdlib.h>#include <memory.h>#include <limits.h>#include <math.h>#include <assert.h>#include "STTypes.h"#include "cpu_detect.h"#include "TDStretch.h"using namespace soundtouch;#ifndef min#define min(a,b) ((a > b) ? b : a)#define max(a,b) ((a < b) ? b : a)#endif/***************************************************************************** * * Constant definitions * *****************************************************************************/#define MAX_SCAN_DELTA 124// Table for the hierarchical mixing position seeking algorithmint scanOffsets[4][24]={ { 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806, 868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0}, {-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};/***************************************************************************** * * Implementation of the class 'TDStretch' * *****************************************************************************/TDStretch::TDStretch() : FIFOProcessor(&outputBuffer){ bQuickseek = FALSE; channels = 2; bMidBufferDirty = FALSE; pMidBuffer = NULL; pRefMidBufferUnaligned = NULL; overlapLength = 0; setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS); setTempo(1.0f);}TDStretch::~TDStretch(){ delete[] pMidBuffer; delete[] pRefMidBufferUnaligned;} // Calculates the x having the closest 2^x value for the given valuestatic int _getClosest2Power(double value){ return (int)(log(value) / log(2.0) + 0.5);}// Sets routine control parameters. These control are certain time constants// defining how the sound is stretched to the desired duration.//// 'sampleRate' = sample rate of the sound// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)// 'seekwindowMS' = seeking window length for scanning the best overlapping // position (default = 28 ms)// 'overlapMS' = overlapping length (default = 12 ms)void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS, uint aSeekWindowMS, uint aOverlapMS){ this->sampleRate = aSampleRate; this->sequenceMs = aSequenceMS; this->seekWindowMs = aSeekWindowMS; this->overlapMs = aOverlapMS; seekLength = (sampleRate * seekWindowMs) / 1000; seekWindowLength = (sampleRate * sequenceMs) / 1000; maxOffset = seekLength; calculateOverlapLength(overlapMs); // set tempo to recalculate 'sampleReq' setTempo(tempo);}/// Get routine control parameters, see setParameters() function./// Any of the parameters to this function can be NULL, in such case corresponding parameter/// value isn't returned.void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs){ if (pSampleRate) { *pSampleRate = sampleRate; } if (pSequenceMs) { *pSequenceMs = sequenceMs; } if (pSeekWindowMs) { *pSeekWindowMs = seekWindowMs; } if (pOverlapMs) { *pOverlapMs = overlapMs; }}// Overlaps samples in 'midBuffer' with the samples in 'input'void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const{ int i, itemp; for (i = 0; i < (int)overlapLength ; i ++) { itemp = overlapLength - i; output[i] = (input[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits; }}void TDStretch::clearMidBuffer(){ if (bMidBufferDirty) { memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength); bMidBufferDirty = FALSE; }}void TDStretch::clearInput(){ inputBuffer.clear(); clearMidBuffer();}// Clears the sample buffersvoid TDStretch::clear(){ outputBuffer.clear(); inputBuffer.clear(); clearMidBuffer();}// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero// to enablevoid TDStretch::enableQuickSeek(BOOL enable){ bQuickseek = enable;}// Returns nonzero if the quick seeking algorithm is enabled.BOOL TDStretch::isQuickSeekEnabled() const{ return bQuickseek;}// Seeks for the optimal overlap-mixing position.uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos){ if (channels == 2) { // stereo sound if (bQuickseek) { return seekBestOverlapPositionStereoQuick(refPos); } else { return seekBestOverlapPositionStereo(refPos); } } else { // mono sound if (bQuickseek) { return seekBestOverlapPositionMonoQuick(refPos); } else { return seekBestOverlapPositionMono(refPos); } }}// Overlaps samples in 'midBuffer' with the samples in 'inputBuffer' at position// of 'ovlPos'.inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const{ if (channels == 2) { // stereo sound overlapStereo(output, input + 2 * ovlPos); } else { // mono sound. overlapMono(output, input + ovlPos); }}// Seeks for the optimal overlap-mixing position. The 'stereo' version of the// routine//// The best position is determined as the position where the two overlapped// sample sequences are 'most alike', in terms of the highest cross-correlation// value over the overlapping perioduint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos) { uint bestOffs; LONG_SAMPLETYPE bestCorr, corr; uint i; // Slopes the amplitudes of the 'midBuffer' samples precalcCorrReferenceStereo(); bestCorr = INT_MIN; bestOffs = 0; // Scans for the best correlation value by testing each possible position // over the permitted range. for (i = 0; i < seekLength; i ++) { // Calculates correlation value for the mixing position corresponding // to 'i' corr = calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer); // Checks for the highest correlation value if (corr > bestCorr) { bestCorr = corr; bestOffs = i; } } // clear cross correlation routine state if necessary (is so e.g. in MMX routines). clearCrossCorrState(); return bestOffs;}// Seeks for the optimal overlap-mixing position. The 'stereo' version of the// routine//// The best position is determined as the position where the two overlapped// sample sequences are 'most alike', in terms of the highest cross-correlation// value over the overlapping perioduint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos) { uint j; uint bestOffs; LONG_SAMPLETYPE bestCorr, corr; uint scanCount, corrOffset, tempOffset; // Slopes the amplitude of the 'midBuffer' samples precalcCorrReferenceStereo(); bestCorr = INT_MIN; bestOffs = 0; corrOffset = 0; tempOffset = 0; // Scans for the best correlation value using four-pass hierarchical search. // // The look-up table 'scans' has hierarchical position adjusting steps. // In first pass the routine searhes for the highest correlation with // relatively coarse steps, then rescans the neighbourhood of the highest // correlation with better resolution and so on. for (scanCount = 0;scanCount < 4; scanCount ++) { j = 0; while (scanOffsets[scanCount][j]) { tempOffset = corrOffset + scanOffsets[scanCount][j]; if (tempOffset >= seekLength) break; // Calculates correlation value for the mixing position corresponding // to 'tempOffset' corr = calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer); // Checks for the highest correlation value if (corr > bestCorr) { bestCorr = corr; bestOffs = tempOffset; } j ++; } corrOffset = bestOffs; } // clear cross correlation routine state if necessary (is so e.g. in MMX routines). clearCrossCorrState(); return bestOffs;}// Seeks for the optimal overlap-mixing position. The 'mono' version of the// routine//// The best position is determined as the position where the two overlapped// sample sequences are 'most alike', in terms of the highest cross-correlation// value over the overlapping perioduint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos) { uint bestOffs; LONG_SAMPLETYPE bestCorr, corr; uint tempOffset; const SAMPLETYPE *compare; // Slopes the amplitude of the 'midBuffer' samples precalcCorrReferenceMono(); bestCorr = INT_MIN; bestOffs = 0; // Scans for the best correlation value by testing each possible position // over the permitted range. for (tempOffset = 0; tempOffset < seekLength; tempOffset ++) { compare = refPos + tempOffset; // Calculates correlation value for the mixing position corresponding // to 'tempOffset' corr = calcCrossCorrMono(pRefMidBuffer, compare); // Checks for the highest correlation value if (corr > bestCorr) { bestCorr = corr; bestOffs = tempOffset; } } // clear cross correlation routine state if necessary (is so e.g. in MMX routines). clearCrossCorrState(); return bestOffs;}// Seeks for the optimal overlap-mixing position. The 'mono' version of the// routine//// The best position is determined as the position where the two overlapped// sample sequences are 'most alike', in terms of the highest cross-correlation// value over the overlapping perioduint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos) { uint j; uint bestOffs; LONG_SAMPLETYPE bestCorr, corr; uint scanCount, corrOffset, tempOffset; // Slopes the amplitude of the 'midBuffer' samples precalcCorrReferenceMono(); bestCorr = INT_MIN; bestOffs = 0; corrOffset = 0; tempOffset = 0; // Scans for the best correlation value using four-pass hierarchical search. // // The look-up table 'scans' has hierarchical position adjusting steps. // In first pass the routine searhes for the highest correlation with // relatively coarse steps, then rescans the neighbourhood of the highest // correlation with better resolution and so on. for (scanCount = 0;scanCount < 4; scanCount ++) { j = 0; while (scanOffsets[scanCount][j]) { tempOffset = corrOffset + scanOffsets[scanCount][j]; if (tempOffset >= seekLength) break; // Calculates correlation value for the mixing position corresponding // to 'tempOffset' corr = calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer); // Checks for the highest correlation value if (corr > bestCorr) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -