# HG changeset patch # User Chris Cannam # Date 1158148604 0 # Node ID cc566264c935be61307a564b45e58fd07429a074 # Parent 085f34c7393915263fdfc2ba58736387127e4e46 * timestretcher improvements -- simplify API (it can calculate its own processing block sizes etc) diff -r 085f34c73939 -r cc566264c935 audioio/AudioCallbackPlaySource.cpp --- a/audioio/AudioCallbackPlaySource.cpp Wed Sep 13 11:06:28 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.cpp Wed Sep 13 11:56:44 2006 +0000 @@ -598,15 +598,11 @@ for (size_t ch = 0; ch < channels; ++ch) { - m_stretcher[ch] = StretcherBuffer -//!!! - (new PhaseVocoderTimeStretcher(factor, - blockSize, + m_stretcher[ch] = new PhaseVocoderTimeStretcher(factor, blockSize); // 128), - (blockSize/2) / factor), - new float[lrintf(blockSize * factor)]); +// (blockSize/2) / factor), +// new float[lrintf(blockSize * factor)]); } - m_stretchInputBuffer = new float[blockSize]; } AudioCallbackPlaySource::TimeStretcherData::~TimeStretcherData() @@ -614,19 +610,19 @@ // std::cerr << "TimeStretcherData::~TimeStretcherData" << std::endl; while (!m_stretcher.empty()) { - delete m_stretcher.begin()->second.first; - delete[] m_stretcher.begin()->second.second; + delete m_stretcher.begin()->second; +// delete[] m_stretcher.begin()->second.second; m_stretcher.erase(m_stretcher.begin()); } - delete m_stretchInputBuffer; +// delete m_stretchInputBuffer; } PhaseVocoderTimeStretcher * AudioCallbackPlaySource::TimeStretcherData::getStretcher(size_t channel) { - return m_stretcher[channel].first; + return m_stretcher[channel]; } - +/* float * AudioCallbackPlaySource::TimeStretcherData::getOutputBuffer(size_t channel) { @@ -646,7 +642,7 @@ getOutputBuffer(channel), m_blockSize); } - +*/ void AudioCallbackPlaySource::setSlowdownFactor(float factor) { @@ -665,7 +661,8 @@ // factor > 1 ? getTargetBlockSize() : getTargetBlockSize() / factor); //!!! doesn't work if the block size > getTargetBlockSize(), but it // should be made to - getTargetBlockSize()); +// getTargetBlockSize()); + lrintf(getTargetBlockSize() / factor)); m_slowdownCounter = 0; m_timeStretcher = newStretcher; } else { diff -r 085f34c73939 -r cc566264c935 audioio/AudioCallbackPlaySource.h --- a/audioio/AudioCallbackPlaySource.h Wed Sep 13 11:06:28 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.h Wed Sep 13 11:56:44 2006 +0000 @@ -252,18 +252,16 @@ float getFactor() const { return m_factor; } PhaseVocoderTimeStretcher *getStretcher(size_t channel); - float *getOutputBuffer(size_t channel); - float *getInputBuffer(); +// float *getOutputBuffer(size_t channel); +// float *getInputBuffer(); - void run(size_t channel); +// void run(size_t channel); protected: TimeStretcherData(const TimeStretcherData &); // not provided TimeStretcherData &operator=(const TimeStretcherData &); // not provided - typedef std::pair StretcherBuffer; - std::map m_stretcher; - float *m_stretchInputBuffer; + std::map m_stretcher; float m_factor; size_t m_blockSize; }; diff -r 085f34c73939 -r cc566264c935 audioio/PhaseVocoderTimeStretcher.cpp --- a/audioio/PhaseVocoderTimeStretcher.cpp Wed Sep 13 11:06:28 2006 +0000 +++ b/audioio/PhaseVocoderTimeStretcher.cpp Wed Sep 13 11:56:44 2006 +0000 @@ -21,18 +21,34 @@ //#define DEBUG_PHASE_VOCODER_TIME_STRETCHER 1 PhaseVocoderTimeStretcher::PhaseVocoderTimeStretcher(float ratio, - size_t maxProcessInputBlockSize, - size_t inputIncrement, - size_t windowSize, - WindowType windowType) : - m_ratio(ratio), - m_n1(inputIncrement), - m_n2(lrintf(m_n1 * ratio)), - m_wlen(std::max(windowSize, m_n2 * 2)), - m_inbuf(m_wlen), - m_outbuf(maxProcessInputBlockSize * ratio + 1024) //!!! + size_t maxProcessInputBlockSize) : + m_ratio(ratio) + //, + // m_n1(inputIncrement), + // m_n2(lrintf(m_n1 * ratio)), + // m_wlen(std::max(windowSize, m_n2 * 2)), + // m_inbuf(m_wlen), + // m_outbuf(maxProcessInputBlockSize * ratio + 1024) //!!! { - m_window = new Window(windowType, m_wlen), + if (ratio < 1) { + m_n1 = 512; + m_n2 = m_n1 * ratio; + m_wlen = 1024; + } else { + m_n2 = 512; + m_n1 = m_n2 / ratio; + m_wlen = 1024; + } + + m_inbuf = new RingBuffer(m_wlen); + m_outbuf = new RingBuffer + (lrintf((maxProcessInputBlockSize + m_wlen) * ratio)); + + std::cerr << "PhaseVocoderTimeStretcher: ratio = " << ratio + << ", n1 = " << m_n1 << ", n2 = " << m_n2 << ", wlen = " + << m_wlen << ", max = " << maxProcessInputBlockSize << ", outbuflen = " << m_outbuf->getSize() << std::endl; + + m_window = new Window(HanningWindow, m_wlen), m_time = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * m_wlen); m_freq = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * m_wlen); @@ -68,6 +84,9 @@ fftwf_free(m_prevPhase); fftwf_free(m_prevAdjustedPhase); + delete m_inbuf; + delete m_outbuf; + delete m_window; } @@ -97,12 +116,12 @@ size_t consumed = 0; #ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "PhaseVocoderTimeStretcher::process(" << samples << ", consumed = " << consumed << "), writable " << m_inbuf.getWriteSpace() <<", readable "<< m_outbuf.getReadSpace() << std::endl; + std::cerr << "PhaseVocoderTimeStretcher::process(" << samples << ", consumed = " << consumed << "), writable " << m_inbuf->getWriteSpace() <<", readable "<< m_outbuf->getReadSpace() << std::endl; #endif while (consumed < samples) { - size_t writable = m_inbuf.getWriteSpace(); + size_t writable = m_inbuf->getWriteSpace(); writable = std::min(writable, samples - consumed); if (writable == 0) { @@ -114,18 +133,18 @@ #ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER std::cerr << "writing " << writable << " from index " << consumed << " to inbuf, consumed will be " << consumed + writable << std::endl; #endif - m_inbuf.write(input + consumed, writable); + m_inbuf->write(input + consumed, writable); consumed += writable; - while (m_inbuf.getReadSpace() >= m_wlen && - m_outbuf.getWriteSpace() >= m_n2) { + while (m_inbuf->getReadSpace() >= m_wlen && + m_outbuf->getWriteSpace() >= m_n2) { // We know we have at least m_wlen samples available - // in m_inbuf. We need to peek m_wlen of them for + // in m_inbuf-> We need to peek m_wlen of them for // processing, and then read m_n1 to advance the read // pointer. - size_t got = m_inbuf.peek(m_dbuf, m_wlen); + size_t got = m_inbuf->peek(m_dbuf, m_wlen); assert(got == m_wlen); processBlock(m_dbuf, m_mashbuf, m_modulationbuf); @@ -133,7 +152,7 @@ #ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER std::cerr << "writing first " << m_n2 << " from mashbuf, skipping " << m_n1 << " on inbuf " << std::endl; #endif - m_inbuf.skip(m_n1); + m_inbuf->skip(m_n1); for (size_t i = 0; i < m_n2; ++i) { if (m_modulationbuf[i] > 0.f) { @@ -141,7 +160,7 @@ } } - m_outbuf.write(m_mashbuf, m_n2); + m_outbuf->write(m_mashbuf, m_n2); for (size_t i = 0; i < m_wlen - m_n2; ++i) { m_mashbuf[i] = m_mashbuf[i + m_n2]; @@ -154,28 +173,28 @@ } } -// std::cerr << "WARNING: PhaseVocoderTimeStretcher::process: writespace not enough for output increment (" << m_outbuf.getWriteSpace() << " < " << m_n2 << ")" << std::endl; +// std::cerr << "WARNING: PhaseVocoderTimeStretcher::process: writespace not enough for output increment (" << m_outbuf->getWriteSpace() << " < " << m_n2 << ")" << std::endl; // } #ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER - std::cerr << "loop ended: inbuf read space " << m_inbuf.getReadSpace() << ", outbuf write space " << m_outbuf.getWriteSpace() << std::endl; + std::cerr << "loop ended: inbuf read space " << m_inbuf->getReadSpace() << ", outbuf write space " << m_outbuf->getWriteSpace() << std::endl; #endif } size_t toRead = lrintf(samples * m_ratio); - if (m_outbuf.getReadSpace() < toRead) { - std::cerr << "WARNING: PhaseVocoderTimeStretcher::process: not enough data (yet?) (" << m_outbuf.getReadSpace() << " < " << toRead << ")" << std::endl; - size_t fill = toRead - m_outbuf.getReadSpace(); + if (m_outbuf->getReadSpace() < toRead) { + std::cerr << "WARNING: PhaseVocoderTimeStretcher::process: not enough data (yet?) (" << m_outbuf->getReadSpace() << " < " << toRead << ")" << std::endl; + size_t fill = toRead - m_outbuf->getReadSpace(); for (size_t i = 0; i < fill; ++i) { output[i] = 0.0; } - m_outbuf.read(output + fill, m_outbuf.getReadSpace()); + m_outbuf->read(output + fill, m_outbuf->getReadSpace()); } else { #ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER std::cerr << "enough data - writing " << toRead << " from outbuf" << std::endl; #endif - m_outbuf.read(output, toRead); + m_outbuf->read(output, toRead); } #ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER @@ -252,8 +271,13 @@ buf[i] /= div; } */ + + float area = m_window->getArea(); + for (i = 0; i < m_wlen; ++i) { out[i] += buf[i]; - modulation[i] += m_window->getValue(i); + float val = m_window->getValue(i); + modulation[i] += val * area; } } + diff -r 085f34c73939 -r cc566264c935 audioio/PhaseVocoderTimeStretcher.h --- a/audioio/PhaseVocoderTimeStretcher.h Wed Sep 13 11:06:28 2006 +0000 +++ b/audioio/PhaseVocoderTimeStretcher.h Wed Sep 13 11:56:44 2006 +0000 @@ -33,41 +33,43 @@ class PhaseVocoderTimeStretcher { public: - PhaseVocoderTimeStretcher(float ratio, - size_t maxProcessInputBlockSize, - size_t inputIncrement = 64, - size_t windowSize = 2048, - WindowType windowType = HanningWindow); + PhaseVocoderTimeStretcher(float ratio, size_t maxProcessInputBlockSize); virtual ~PhaseVocoderTimeStretcher(); /** * Process a block. The input array contains the given number of - * samples; the output has enough space for samples * m_ratio. + * samples; the output must have space for lrintf(samples * m_ratio). */ void process(float *input, float *output, size_t samples); /** - * Get the hop size for input. Smaller values may produce better - * results, at a cost in processing time. Larger values are - * faster but increase the likelihood of echo-like effects. The - * default is 64, which is usually pretty good, though heavy on - * processor power. + * Get the hop size for input. */ size_t getInputIncrement() const { return m_n1; } /** - * Get the window size for FFT processing. Must be larger than - * the input and output increments. The default is 2048. + * Get the hop size for output. + */ + size_t getOutputIncrement() const { return getInputIncrement() * getRatio(); } + + /** + * Get the window size for FFT processing. */ size_t getWindowSize() const { return m_wlen; } /** - * Get the window type. The default is a Hanning window. + * Get the window type. */ WindowType getWindowType() const { return m_window->getType(); } + /** + * Get the stretch ratio set in the constructor. + */ float getRatio() const { return m_ratio; } - size_t getOutputIncrement() const { return getInputIncrement() * getRatio(); } + + /** + * Get the latency added by the time stretcher, in sample frames. + */ size_t getProcessingLatency() const; protected: @@ -101,8 +103,8 @@ fftwf_plan m_plan; fftwf_plan m_iplan; - RingBuffer m_inbuf; - RingBuffer m_outbuf; + RingBuffer *m_inbuf; + RingBuffer *m_outbuf; float *m_mashbuf; float *m_modulationbuf; };