changeset 553:2a1e9e017484 bqresample

Fixes to sample rate and latency handling
author Chris Cannam
date Fri, 09 Dec 2016 14:40:49 +0000
parents 8c11ca1ebc39
children 4be200469a9c
files audio/AudioCallbackPlaySource.cpp audio/AudioCallbackPlaySource.h
diffstat 2 files changed, 43 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/audio/AudioCallbackPlaySource.cpp	Fri Dec 09 13:46:34 2016 +0000
+++ b/audio/AudioCallbackPlaySource.cpp	Fri Dec 09 14:40:49 2016 +0000
@@ -54,7 +54,7 @@
     m_sourceChannelCount(0),
     m_blockSize(1024),
     m_sourceSampleRate(0),
-    m_targetSampleRate(0),
+    m_deviceSampleRate(0),
     m_playLatency(0),
     m_target(0),
     m_lastRetrievalTimestamp(0.0),
@@ -231,7 +231,9 @@
     }
 
     if (srChanged) {
+
         SVCERR << "AudioCallbackPlaySource: Source rate changed" << endl;
+
         if (m_resamplerWrapper) {
             SVCERR << "AudioCallbackPlaySource: Source sample rate changed to "
                 << m_sourceSampleRate << ", updating resampler wrapper" << endl;
@@ -239,6 +241,15 @@
                 (int(round(m_sourceSampleRate)));
             m_resamplerWrapper->reset();
         }
+
+        delete m_timeStretcher;
+        delete m_monoStretcher;
+        m_timeStretcher = 0;
+        m_monoStretcher = 0;
+        
+        if (m_stretchRatio != 1.f) {
+            setTimeStretch(m_stretchRatio);
+        }
     }
 
     rebuildRangeLists();
@@ -632,12 +643,12 @@
     // This method attempts to estimate which audio sample frame is
     // "currently coming through the speakers".
 
-    sv_samplerate_t targetRate = getTargetSampleRate();
+    sv_samplerate_t deviceRate = getDeviceSampleRate();
     sv_frame_t latency = m_playLatency; // at target rate
     RealTime latency_t = RealTime::zeroTime;
 
-    if (targetRate != 0) {
-        latency_t = RealTime::frame2RealTime(latency, targetRate);
+    if (deviceRate != 0) {
+        latency_t = RealTime::frame2RealTime(latency, deviceRate);
     }
 
     return getCurrentFrame(latency_t);
@@ -652,16 +663,18 @@
 sv_frame_t
 AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t)
 {
-    // We resample when filling the ring buffer, and time-stretch when
-    // draining it.  The buffer contains data at the "target rate" and
-    // the latency provided by the target is also at the target rate.
-    // Because of the multiple rates involved, we do the actual
-    // calculation using RealTime instead.
+    // The ring buffers contain data at the source sample rate and all
+    // processing (including time stretching) happens at this
+    // rate. Resampling only happens after the audio data leaves this
+    // class.
+    
+    // (But because historically more than one sample rate could have
+    // been involved here, we do latency calculations using RealTime
+    // values instead of samples.)
 
-    sv_samplerate_t sourceRate = getSourceSampleRate();
-    sv_samplerate_t targetRate = getTargetSampleRate();
+    sv_samplerate_t rate = getSourceSampleRate();
 
-    if (sourceRate == 0 || targetRate == 0) return 0;
+    if (rate == 0) return 0;
 
     int inbuffer = 0; // at target rate
 
@@ -681,7 +694,7 @@
 
     bool looping = m_viewManager->getPlayLoopMode();
 
-    RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate);
+    RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, rate);
 
     sv_frame_t stretchlat = 0;
     double timeRatio = 1.0;
@@ -691,7 +704,7 @@
         timeRatio = m_timeStretcher->getTimeRatio();
     }
 
-    RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, targetRate);
+    RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, rate);
 
     // When the target has just requested a block from us, the last
     // sample it obtained was our buffer fill frame count minus the
@@ -709,8 +722,7 @@
         m_trustworthyTimestamps &&
         lastRetrievalTimestamp != 0.0) {
 
-        lastretrieved_t = RealTime::frame2RealTime
-            (lastRetrievedBlockSize, targetRate);
+        lastretrieved_t = RealTime::frame2RealTime(lastRetrievedBlockSize, rate);
 
         // calculate number of frames at target rate that have elapsed
         // since the end of the last call to getSourceSamples
@@ -727,11 +739,10 @@
 
     } else {
 
-        lastretrieved_t = RealTime::frame2RealTime
-            (getTargetBlockSize(), targetRate);
+        lastretrieved_t = RealTime::frame2RealTime(getTargetBlockSize(), rate);
     }
 
-    RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, sourceRate);
+    RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, rate);
 
     if (timeRatio != 1.0) {
         lastretrieved_t = lastretrieved_t / timeRatio;
@@ -757,7 +768,7 @@
             - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t
             + sincerequest_t;
         if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime;
-        sv_frame_t frame = RealTime::realTime2Frame(playing_t, sourceRate);
+        sv_frame_t frame = RealTime::realTime2Frame(playing_t, rate);
         return m_viewManager->alignPlaybackFrameToReference(frame);
     }
 
@@ -794,8 +805,7 @@
     // duration of playback!
 
     if (!m_playStartFramePassed) {
-        RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame,
-                                                        sourceRate);
+        RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame, rate);
         if (playing_t < playstart_t) {
 //            cerr << "playing_t " << playing_t << " < playstart_t " 
 //                      << playstart_t << endl;
@@ -853,7 +863,7 @@
 
     if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime;
 
-    sv_frame_t frame = RealTime::realTime2Frame(playing_t, sourceRate);
+    sv_frame_t frame = RealTime::realTime2Frame(playing_t, rate);
 
     if (m_lastCurrentFrame > 0 && !looping) {
         if (frame < m_lastCurrentFrame) {
@@ -938,15 +948,7 @@
 void
 AudioCallbackPlaySource::setSystemPlaybackSampleRate(int sr)
 {
-    bool first = (m_targetSampleRate == 0);
-
-    m_targetSampleRate = sr;
-
-    if (first && (m_stretchRatio != 1.f)) {
-        // couldn't create a stretcher before because we had no sample
-        // rate: make one now
-        setTimeStretch(m_stretchRatio);
-    }
+    m_deviceSampleRate = sr;
 }
 
 void
@@ -983,10 +985,9 @@
 }
 
 sv_samplerate_t
-AudioCallbackPlaySource::getTargetSampleRate() const
+AudioCallbackPlaySource::getDeviceSampleRate() const
 {
-    if (m_targetSampleRate) return m_targetSampleRate;
-    else return getSourceSampleRate();
+    return m_deviceSampleRate;
 }
 
 int
@@ -1013,19 +1014,20 @@
 {
     m_stretchRatio = factor;
 
-    if (!getTargetSampleRate()) return; // have to make our stretcher later
+    int rate = int(getSourceSampleRate());
+    if (!rate) return; // have to make our stretcher later
 
     if (m_timeStretcher || (factor == 1.0)) {
         // stretch ratio will be set in next process call if appropriate
     } else {
         m_stretcherInputCount = getTargetChannelCount();
         RubberBandStretcher *stretcher = new RubberBandStretcher
-            (int(getTargetSampleRate()),
+            (rate,
              m_stretcherInputCount,
              RubberBandStretcher::OptionProcessRealTime,
              factor);
         RubberBandStretcher *monoStretcher = new RubberBandStretcher
-            (int(getTargetSampleRate()),
+            (rate,
              1,
              RubberBandStretcher::OptionProcessRealTime,
              factor);
--- a/audio/AudioCallbackPlaySource.h	Fri Dec 09 13:46:34 2016 +0000
+++ b/audio/AudioCallbackPlaySource.h	Fri Dec 09 14:40:49 2016 +0000
@@ -145,7 +145,7 @@
 
     /**
      * Set the playback latency of the target audio device, in frames
-     * at the target sample rate.  This is the difference between the
+     * at the device sample rate.  This is the difference between the
      * frame currently "leaving the speakers" and the last frame (or
      * highest last frame across all channels) requested via
      * getSamples().  The default is zero.
@@ -170,7 +170,7 @@
      * Return the sample rate set by the target audio device (or the
      * source sample rate if the target hasn't set one).
      */
-    virtual sv_samplerate_t getTargetSampleRate() const;
+    sv_samplerate_t getDeviceSampleRate() const;
 
     /**
      * Indicate how many channels the target audio device was opened
@@ -335,7 +335,7 @@
     int                               m_sourceChannelCount;
     sv_frame_t                        m_blockSize;
     sv_samplerate_t                   m_sourceSampleRate;
-    sv_samplerate_t                   m_targetSampleRate;
+    sv_samplerate_t                   m_deviceSampleRate;
     sv_frame_t                        m_playLatency;
     breakfastquay::SystemPlaybackTarget *m_target;
     double                            m_lastRetrievalTimestamp;