diff audioio/AudioCallbackPlaySource.cpp @ 16:3715efc38f95

* substantial enhancements to time stretcher: -- use putInput/getOutput methods to ensure the audio source always feeds it enough input, avoiding underruns due to rounding error -- add a percussion detector and an optional "Sharpen" toggle to the main window, which invokes a very basic variable speed timestretcher
author Chris Cannam
date Wed, 13 Sep 2006 17:17:42 +0000
parents cc566264c935
children 80126455d169
line wrap: on
line diff
--- a/audioio/AudioCallbackPlaySource.cpp	Wed Sep 13 11:56:44 2006 +0000
+++ b/audioio/AudioCallbackPlaySource.cpp	Wed Sep 13 17:17:42 2006 +0000
@@ -51,7 +51,6 @@
     m_lastModelEndFrame(0),
     m_outputLeft(0.0),
     m_outputRight(0.0),
-    m_slowdownCounter(0),
     m_timeStretcher(0),
     m_fillThread(0),
     m_converter(0)
@@ -427,10 +426,10 @@
 
     size_t latency = m_playLatency;
     if (resample) latency = size_t(m_playLatency * ratio + 0.1);
-    
-    TimeStretcherData *timeStretcher = m_timeStretcher;
+
+    PhaseVocoderTimeStretcher *timeStretcher = m_timeStretcher;
     if (timeStretcher) {
-	latency += timeStretcher->getStretcher(0)->getProcessingLatency();
+	latency += timeStretcher->getProcessingLatency();
     }
 
     latency += readSpace;
@@ -588,82 +587,26 @@
     return m_sourceSampleRate;
 }
 
-AudioCallbackPlaySource::TimeStretcherData::TimeStretcherData(size_t channels,
-							      float factor,
-							      size_t blockSize) :
-    m_factor(factor),
-    m_blockSize(blockSize)
-{
-//    std::cerr << "TimeStretcherData::TimeStretcherData(" << channels << ", " << factor << ", " << blockSize << ")" << std::endl;
-
-    for (size_t ch = 0; ch < channels; ++ch) {
-
-	m_stretcher[ch] = new PhaseVocoderTimeStretcher(factor, blockSize);
-//                                      128),
-//                                      (blockSize/2) / factor),
-//	     new float[lrintf(blockSize * factor)]);
-    }
-}
-
-AudioCallbackPlaySource::TimeStretcherData::~TimeStretcherData()
-{
-//    std::cerr << "TimeStretcherData::~TimeStretcherData" << std::endl;
-
-    while (!m_stretcher.empty()) {
-	delete m_stretcher.begin()->second;
-//	delete[] m_stretcher.begin()->second.second;
-	m_stretcher.erase(m_stretcher.begin());
-    }
-//    delete m_stretchInputBuffer;
-}
-
-PhaseVocoderTimeStretcher *
-AudioCallbackPlaySource::TimeStretcherData::getStretcher(size_t channel)
-{
-    return m_stretcher[channel];
-}
-/*
-float *
-AudioCallbackPlaySource::TimeStretcherData::getOutputBuffer(size_t channel)
-{
-    return m_stretcher[channel].second;
-}
-
-float *
-AudioCallbackPlaySource::TimeStretcherData::getInputBuffer()
-{
-    return m_stretchInputBuffer;
-}
-
 void
-AudioCallbackPlaySource::TimeStretcherData::run(size_t channel)
-{
-    getStretcher(channel)->process(getInputBuffer(),
-				   getOutputBuffer(channel),
-				   m_blockSize);
-}
-*/
-void
-AudioCallbackPlaySource::setSlowdownFactor(float factor)
+AudioCallbackPlaySource::setSlowdownFactor(float factor, bool sharpen)
 {
     // Avoid locks -- create, assign, mark old one for scavenging
     // later (as a call to getSourceSamples may still be using it)
 
-    TimeStretcherData *existingStretcher = m_timeStretcher;
+    PhaseVocoderTimeStretcher *existingStretcher = m_timeStretcher;
 
-    if (existingStretcher && existingStretcher->getFactor() == factor) {
+    if (existingStretcher &&
+        existingStretcher->getRatio() == factor &&
+        existingStretcher->getSharpening() == sharpen) {
 	return;
     }
 
     if (factor != 1) {
-	TimeStretcherData *newStretcher = new TimeStretcherData
-	    (getTargetChannelCount(), factor,
-//             factor > 1 ? getTargetBlockSize() : getTargetBlockSize() / factor);
-             //!!! doesn't work if the block size > getTargetBlockSize(), but it
-             // should be made to
-//             getTargetBlockSize());
+	PhaseVocoderTimeStretcher *newStretcher = new PhaseVocoderTimeStretcher
+	    (getTargetChannelCount(),
+             factor,
+             sharpen,
              lrintf(getTargetBlockSize() / factor));
-	m_slowdownCounter = 0;
 	m_timeStretcher = newStretcher;
     } else {
 	m_timeStretcher = 0;
@@ -686,9 +629,9 @@
 	return 0;
     }
 
-    TimeStretcherData *timeStretcher = m_timeStretcher;
+    PhaseVocoderTimeStretcher *ts = m_timeStretcher;
 
-    if (!timeStretcher || timeStretcher->getFactor() == 1) {
+    if (!ts || ts->getRatio() == 1) {
 
 	size_t got = 0;
 
@@ -721,91 +664,102 @@
 	return got;
     }
 
-/*!!!
-    if (m_slowdownCounter == 0) {
+    float ratio = ts->getRatio();
 
-	size_t got = 0;
-	float *ib = timeStretcher->getInputBuffer();
+//            std::cout << "ratio = " << ratio << std::endl;
 
-	for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
+    size_t available;
 
-	    RingBuffer<float> *rb = getReadRingBuffer(ch);
+    while ((available = ts->getAvailableOutputSamples()) < count) {
 
-	    if (rb) {
+        size_t reqd = lrintf((count - available) / ratio);
+        reqd = std::max(reqd, ts->getRequiredInputSamples());
+        if (reqd == 0) reqd = 1;
+                
+        size_t channels = getTargetChannelCount();
 
-		size_t request = count;
-		if (ch > 0) request = got; // see above
-		got = rb->read(buffer[ch], request);
+        float *ib[channels];
 
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-		std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " samples on channel " << ch << ", running time stretcher" << std::endl;
-#endif
+        size_t got = reqd;
 
-		for (size_t i = 0; i < count; ++i) {
-		    ib[i] = buffer[ch][i];
-		}
-	    
-		timeStretcher->run(ch);
-	    }
-	}
+        for (size_t c = 0; c < channels; ++c) {
+            ib[c] = new float[reqd]; //!!! fix -- this is a rt function
+            RingBuffer<float> *rb = getReadRingBuffer(c);
+            if (rb) {
+                size_t gotHere = rb->read(ib[c], got);
+                if (gotHere < got) got = gotHere;
+            }
+        }
 
-    } else if (m_slowdownCounter >= timeStretcher->getFactor()) {
-	// reset this in case the factor has changed leaving the
-	// counter out of range
-	m_slowdownCounter = 0;
+        if (got < reqd) {
+            std::cerr << "WARNING: Read underrun in playback ("
+                      << got << " < " << reqd << ")" << std::endl;
+        }
+                
+        ts->putInput(ib, got);
+
+        for (size_t c = 0; c < channels; ++c) {
+            delete[] ib[c];
+        }
+
+        if (got == 0) break;
+
+        if (ts->getAvailableOutputSamples() == available) {
+            std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples" << std::endl;
+            break;
+        }
     }
 
-    for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
+    ts->getOutput(buffer, count);
 
-	float *ob = timeStretcher->getOutputBuffer(ch);
 
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-	std::cerr << "AudioCallbackPlaySource::getSamples: Copying from (" << (m_slowdownCounter * count) << "," << count << ") to buffer" << std::endl;
-#endif
-
-	for (size_t i = 0; i < count; ++i) {
-	    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 ratio = ts->getRatio();
 
-            float *ib = new float[request]; //!!!
+//            std::cout << "ratio = " << ratio << std::endl;
 
-            size_t got = rb->read(ib, request);
+            size_t available;
+
+            while ((available = ts->getAvailableOutputSamples()) < count) {
+
+                size_t reqd = lrintf((count - available) / ratio);
+                reqd = std::max(reqd, ts->getRequiredInputSamples());
+                if (reqd == 0) reqd = 1;
+
+                float ib[reqd];
+                size_t got = rb->read(ib, reqd);
 
 #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;
+                std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " samples on channel " << ch << " (reqd=" << reqd << ", count=" << count << ", ratio=" << ratio << ", got*ratio=" << got * ratio << "), running time stretcher" << std::endl;
 #endif
 
-            timeStretcher->getStretcher(ch)->process(ib, buffer[ch], request);
-            
-            delete[] ib;
+                if (got < reqd) {
+                    std::cerr << "WARNING: Read underrun in playback ("
+                              << got << " < " << reqd << ")" << std::endl;
+                }
+                
+                ts->putInput(ib, got);
 
-//            for (size_t i = 0; i < count; ++i) {
-//                ib[i] = buffer[ch][i];
-//            }
-	    
-//            timeStretcher->run(ch);
+                if (got == 0) break;
 
-            
+                if (ts->getAvailableOutputSamples() == available) {
+                    std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples" << std::endl;
+		    break;
+                }
+            }
 
+            ts->getOutput(buffer[ch], count);
         }
     }
+*/
+    m_condition.wakeAll();
 
-    
-
-//!!!    if (m_slowdownCounter == 0) m_condition.wakeAll();
-//    m_slowdownCounter = (m_slowdownCounter + 1) % timeStretcher->getFactor();
     return count;
 }