# HG changeset patch # User Chris Cannam # Date 1158079380 0 # Node ID ee967635c728ba1eae3f2fcde713a7150deade40 # Parent 0dbd08e365cec4d24ecb9509aa5c6bebba1adbbc * Some work on making the time stretcher squash as well as stretch diff -r 0dbd08e365ce -r ee967635c728 audioio/AudioCallbackPlaySource.cpp --- a/audioio/AudioCallbackPlaySource.cpp Tue Sep 12 12:15:44 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.cpp Tue Sep 12 16:43:00 2006 +0000 @@ -28,7 +28,7 @@ #include //#define DEBUG_AUDIO_PLAY_SOURCE 1 -//#define DEBUG_AUDIO_PLAY_SOURCE_PLAYING 1 +#define DEBUG_AUDIO_PLAY_SOURCE_PLAYING 1 //const size_t AudioCallbackPlaySource::m_ringBufferSize = 102400; const size_t AudioCallbackPlaySource::m_ringBufferSize = 131071; @@ -589,7 +589,7 @@ } AudioCallbackPlaySource::TimeStretcherData::TimeStretcherData(size_t channels, - size_t factor, + float factor, size_t blockSize) : m_factor(factor), m_blockSize(blockSize) @@ -601,8 +601,8 @@ //!!! We really need to measure performance and work out //what sort of quality level to use -- or at least to //allow the user to configure it - (new IntegerTimeStretcher(factor, blockSize, 128), - new float[blockSize * factor]); + (new IntegerTimeStretcher(factor, blockSize, 1024), + new float[lrintf(blockSize * factor)]); } m_stretchInputBuffer = new float[blockSize]; } @@ -646,7 +646,7 @@ } void -AudioCallbackPlaySource::setSlowdownFactor(size_t factor) +AudioCallbackPlaySource::setSlowdownFactor(float factor) { // Avoid locks -- create, assign, mark old one for scavenging // later (as a call to getSourceSamples may still be using it) @@ -657,9 +657,10 @@ return; } - if (factor > 1) { + if (factor != 1) { TimeStretcherData *newStretcher = new TimeStretcherData - (getTargetChannelCount(), factor, getTargetBlockSize()); + (getTargetChannelCount(), factor, + factor > 1 ? getTargetBlockSize() : getTargetBlockSize() / factor); m_slowdownCounter = 0; m_timeStretcher = newStretcher; } else { @@ -718,6 +719,7 @@ return got; } +/*!!! if (m_slowdownCounter == 0) { size_t got = 0; @@ -733,7 +735,7 @@ if (ch > 0) request = got; // see above got = rb->read(buffer[ch], request); -#ifdef DEBUG_AUDIO_PLAY_SOURCE +#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " samples on channel " << ch << ", running time stretcher" << std::endl; #endif @@ -755,7 +757,7 @@ float *ob = timeStretcher->getOutputBuffer(ch); -#ifdef DEBUG_AUDIO_PLAY_SOURCE +#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING std::cerr << "AudioCallbackPlaySource::getSamples: Copying from (" << (m_slowdownCounter * count) << "," << count << ") to buffer" << std::endl; #endif @@ -763,9 +765,45 @@ buffer[ch][i] = ob[m_slowdownCounter * count + i]; } } +*/ + + for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) { + + RingBuffer *rb = getReadRingBuffer(ch); + + if (rb) { + + float ratio = timeStretcher->getStretcher(ch)->getRatio(); + size_t request = lrintf(count / ratio); +// if (ch > 0) request = got; // see above + + float *ib = new float[request]; //!!! + + size_t got = rb->read(ib, request); + +#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING + std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " samples on channel " << ch << " (count=" << count << ", ratio=" << timeStretcher->getStretcher(ch)->getRatio() << ", got*ratio=" << got * ratio << "), running time stretcher" << std::endl; +#endif + + timeStretcher->getStretcher(ch)->process(ib, buffer[ch], request); + + delete[] ib; + +// for (size_t i = 0; i < count; ++i) { +// ib[i] = buffer[ch][i]; +// } + +// timeStretcher->run(ch); + + + + } + } + + //!!! if (m_slowdownCounter == 0) m_condition.wakeAll(); - m_slowdownCounter = (m_slowdownCounter + 1) % timeStretcher->getFactor(); +// m_slowdownCounter = (m_slowdownCounter + 1) % timeStretcher->getFactor(); return count; } diff -r 0dbd08e365ce -r ee967635c728 audioio/AudioCallbackPlaySource.h --- a/audioio/AudioCallbackPlaySource.h Tue Sep 12 12:15:44 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.h Tue Sep 12 16:43:00 2006 +0000 @@ -177,7 +177,7 @@ */ size_t getSourceSamples(size_t count, float **buffer); - void setSlowdownFactor(size_t factor); + void setSlowdownFactor(float factor); signals: void modelReplaced(); @@ -247,10 +247,10 @@ class TimeStretcherData { public: - TimeStretcherData(size_t channels, size_t factor, size_t blockSize); + TimeStretcherData(size_t channels, float factor, size_t blockSize); ~TimeStretcherData(); - size_t getFactor() const { return m_factor; } + float getFactor() const { return m_factor; } IntegerTimeStretcher *getStretcher(size_t channel); float *getOutputBuffer(size_t channel); float *getInputBuffer(); @@ -264,7 +264,7 @@ typedef std::pair StretcherBuffer; std::map m_stretcher; float *m_stretchInputBuffer; - size_t m_factor; + float m_factor; size_t m_blockSize; }; diff -r 0dbd08e365ce -r ee967635c728 audioio/IntegerTimeStretcher.cpp --- a/audioio/IntegerTimeStretcher.cpp Tue Sep 12 12:15:44 2006 +0000 +++ b/audioio/IntegerTimeStretcher.cpp Tue Sep 12 16:43:00 2006 +0000 @@ -20,30 +20,34 @@ //#define DEBUG_INTEGER_TIME_STRETCHER 1 -IntegerTimeStretcher::IntegerTimeStretcher(size_t ratio, +IntegerTimeStretcher::IntegerTimeStretcher(float ratio, size_t maxProcessInputBlockSize, size_t inputIncrement, size_t windowSize, WindowType windowType) : m_ratio(ratio), m_n1(inputIncrement), - m_n2(m_n1 * ratio), + m_n2(lrintf(m_n1 * ratio)), m_wlen(std::max(windowSize, m_n2 * 2)), m_inbuf(m_wlen), - m_outbuf(maxProcessInputBlockSize * ratio) + m_outbuf(maxProcessInputBlockSize * ratio + 1024) //!!! { m_window = new Window(windowType, m_wlen), m_time = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * m_wlen); m_freq = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * m_wlen); m_dbuf = (float *)fftwf_malloc(sizeof(float) * m_wlen); + m_mashbuf = (float *)fftwf_malloc(sizeof(float) * m_wlen); + m_prevPhase = (float *)fftwf_malloc(sizeof(float) * m_wlen); + m_prevAdjustedPhase = (float *)fftwf_malloc(sizeof(float) * m_wlen); m_plan = fftwf_plan_dft_1d(m_wlen, m_time, m_freq, FFTW_FORWARD, FFTW_ESTIMATE); m_iplan = fftwf_plan_dft_c2r_1d(m_wlen, m_freq, m_dbuf, FFTW_ESTIMATE); - m_mashbuf = new float[m_wlen]; for (int i = 0; i < m_wlen; ++i) { m_mashbuf[i] = 0.0; + m_prevPhase[i] = 0.0; + m_prevAdjustedPhase[i] = 0.0; } } @@ -57,9 +61,11 @@ fftwf_free(m_time); fftwf_free(m_freq); fftwf_free(m_dbuf); + fftwf_free(m_mashbuf); + fftwf_free(m_prevPhase); + fftwf_free(m_prevAdjustedPhase); delete m_window; - delete m_mashbuf; } size_t @@ -143,18 +149,20 @@ #endif } - if (m_outbuf.getReadSpace() < samples * m_ratio) { - std::cerr << "WARNING: IntegerTimeStretcher::process: not enough data (yet?) (" << m_outbuf.getReadSpace() << " < " << (samples * m_ratio) << ")" << std::endl; - size_t fill = samples * m_ratio - m_outbuf.getReadSpace(); + size_t toRead = lrintf(samples * m_ratio); + + if (m_outbuf.getReadSpace() < toRead) { + std::cerr << "WARNING: IntegerTimeStretcher::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()); } else { #ifdef DEBUG_INTEGER_TIME_STRETCHER - std::cerr << "enough data - writing " << samples * m_ratio << " from outbuf" << std::endl; + std::cerr << "enough data - writing " << toRead << " from outbuf" << std::endl; #endif - m_outbuf.read(output, samples * m_ratio); + m_outbuf.read(output, toRead); } #ifdef DEBUG_INTEGER_TIME_STRETCHER @@ -194,14 +202,25 @@ float mag = sqrtf(m_freq[i][0] * m_freq[i][0] + m_freq[i][1] * m_freq[i][1]); - float phase = atan2f(m_freq[i][1], m_freq[i][0]); + float phase = princargf(atan2f(m_freq[i][1], m_freq[i][0])); + + float omega = (2 * M_PI * m_n1 * i) / m_wlen; - phase = phase * m_ratio; + float expectedPhase = m_prevPhase[i] + omega; + + float phaseError = princargf(phase - expectedPhase); + + float phaseIncrement = (omega + phaseError) / m_n1; + + float adjustedPhase = m_prevAdjustedPhase[i] + m_n2 * phaseIncrement; - float real = mag * cosf(phase); - float imag = mag * sinf(phase); + float real = mag * cosf(adjustedPhase); + float imag = mag * sinf(adjustedPhase); m_freq[i][0] = real; m_freq[i][1] = imag; + + m_prevPhase[i] = phase; + m_prevAdjustedPhase[i] = adjustedPhase; } fftwf_execute(m_iplan); // m_freq -> in, inverse fft diff -r 0dbd08e365ce -r ee967635c728 audioio/IntegerTimeStretcher.h --- a/audioio/IntegerTimeStretcher.h Tue Sep 12 12:15:44 2006 +0000 +++ b/audioio/IntegerTimeStretcher.h Tue Sep 12 16:43:00 2006 +0000 @@ -34,13 +34,17 @@ class IntegerTimeStretcher { public: - IntegerTimeStretcher(size_t ratio, + IntegerTimeStretcher(float ratio, size_t maxProcessInputBlockSize, size_t inputIncrement = 64, size_t windowSize = 2048, WindowType windowType = HanningWindow); virtual ~IntegerTimeStretcher(); + /** + * Process a block. The input array contains the given number of + * samples; the output has enough space for samples * m_ratio. + */ void process(float *input, float *output, size_t samples); /** @@ -63,14 +67,14 @@ */ WindowType getWindowType() const { return m_window->getType(); } - size_t getRatio() const { return m_ratio; } + float getRatio() const { return m_ratio; } size_t getOutputIncrement() const { return getInputIncrement() * getRatio(); } size_t getProcessingLatency() const; protected: void processBlock(float *in, float *out); - size_t m_ratio; + float m_ratio; size_t m_n1; size_t m_n2; size_t m_wlen; @@ -79,6 +83,8 @@ fftwf_complex *m_time; fftwf_complex *m_freq; float *m_dbuf; + float *m_prevPhase; + float *m_prevAdjustedPhase; fftwf_plan m_plan; fftwf_plan m_iplan; diff -r 0dbd08e365ce -r ee967635c728 main/MainWindow.cpp --- a/main/MainWindow.cpp Tue Sep 12 12:15:44 2006 +0000 +++ b/main/MainWindow.cpp Tue Sep 12 16:43:00 2006 +0000 @@ -153,8 +153,8 @@ m_fader = new Fader(frame, false); m_playSpeed = new AudioDial(frame); - m_playSpeed->setMinimum(1); - m_playSpeed->setMaximum(10); + m_playSpeed->setMinimum(0); + m_playSpeed->setMaximum(20); m_playSpeed->setValue(10); m_playSpeed->setFixedWidth(24); m_playSpeed->setFixedHeight(24); @@ -2853,9 +2853,21 @@ void MainWindow::playSpeedChanged(int speed) { - int factor = 11 - speed; + static float factors[] = { + 1.0, 1.1, 1.2, 1.3, 1.5, 1.7, 2.0, 3.0, 4.0, 6.0, 10.0 + }; + float factor = factors[speed >= 10 ? speed - 10 : 10 - speed]; +// int factor = 11 - speed; + if (speed > 10) factor = 1.0 / factor; + std::cerr << "factor = " << factor << std::endl; + + int iinc = 128; + int oinc = lrintf(iinc * factor); + factor = (float(oinc) + 0.01) / iinc; + std::cerr << "corrected factor = " << factor << std::endl; + m_playSpeed->setToolTip(tr("Playback speed: %1") - .arg(factor > 1 ? + .arg(factor != 1 ? QString("1/%1").arg(factor) : tr("Full"))); m_playSource->setSlowdownFactor(factor);