changeset 12:ee967635c728

* Some work on making the time stretcher squash as well as stretch
author Chris Cannam
date Tue, 12 Sep 2006 16:43:00 +0000
parents 0dbd08e365ce
children 00ed645f4175
files audioio/AudioCallbackPlaySource.cpp audioio/AudioCallbackPlaySource.h audioio/IntegerTimeStretcher.cpp audioio/IntegerTimeStretcher.h main/MainWindow.cpp
diffstat 5 files changed, 110 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- 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 <cassert>
 
 //#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<float> *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;
 }
 
--- 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<IntegerTimeStretcher *, float *> StretcherBuffer;
 	std::map<size_t, StretcherBuffer> m_stretcher;
 	float *m_stretchInputBuffer;
-	size_t m_factor;
+	float m_factor;
 	size_t m_blockSize;
     };
 
--- 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<float>(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
--- 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;
--- 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);