Mercurial > hg > svapp
changeset 93:737b373246b5
* Further fixes to the handling of playback frame and buffered frame counts
author | Chris Cannam |
---|---|
date | Mon, 11 Feb 2008 12:46:39 +0000 |
parents | 792bca285459 |
children | 9cc9862333bd |
files | audioio/AudioCallbackPlaySource.cpp audioio/AudioCallbackPlaySource.h framework/MainWindowBase.cpp |
diffstat | 3 files changed, 155 insertions(+), 93 deletions(-) [+] |
line wrap: on
line diff
--- a/audioio/AudioCallbackPlaySource.cpp Fri Feb 08 17:54:49 2008 +0000 +++ b/audioio/AudioCallbackPlaySource.cpp Mon Feb 11 12:46:39 2008 +0000 @@ -244,7 +244,9 @@ #ifdef DEBUG_AUDIO_PLAY_SOURCE std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl; #endif - if (endFrame > m_lastModelEndFrame) m_lastModelEndFrame = endFrame; + if (endFrame > m_lastModelEndFrame) { + m_lastModelEndFrame = endFrame; + } } void @@ -312,6 +314,8 @@ m_mutex.unlock(); m_audioGenerator->clearModels(); + + clearRingBuffers(); } void @@ -319,22 +323,13 @@ { if (!haveLock) m_mutex.lock(); + rebuildRangeLists(); + if (count == 0) { if (m_writeBuffers) count = m_writeBuffers->size(); } - size_t sf = m_readBufferFill; - RingBuffer<float> *rb = getReadRingBuffer(0); - if (rb) { - //!!! This is incorrect if we're in a non-contiguous selection - //Same goes for all related code (subtracting the read space - //from the fill frame to try to establish where the effective - //pre-resample/timestretch read pointer is) - size_t rs = rb->getReadSpace(); - if (rs < sf) sf -= rs; - else sf = 0; - } - m_writeBufferFill = sf; + m_writeBufferFill = getCurrentBufferedFrame(); if (m_readBuffers != m_writeBuffers) { delete m_writeBuffers; @@ -383,10 +378,12 @@ m_timeStretcher->reset(); } if (m_playing) { + std::cerr << "playing already, resetting" << std::endl; m_readBufferFill = m_writeBufferFill = startFrame; if (m_readBuffers) { for (size_t c = 0; c < getTargetChannelCount(); ++c) { RingBuffer<float> *rb = getReadRingBuffer(c); + std::cerr << "reset ring buffer for channel " << c << std::endl; if (rb) rb->reset(); } } @@ -498,6 +495,22 @@ // This method attempts to estimate which audio sample frame is // "currently coming through the speakers". + size_t targetRate = getTargetSampleRate(); + size_t latency = m_playLatency; // at target rate + RealTime latency_t = RealTime::frame2RealTime(latency, targetRate); + + return getCurrentFrame(latency_t); +} + +size_t +AudioCallbackPlaySource::getCurrentBufferedFrame() +{ + return getCurrentFrame(RealTime::zeroTime); +} + +size_t +AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t) +{ bool resample = false; double resampleRatio = 1.0; @@ -530,9 +543,6 @@ RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate); - size_t latency = m_playLatency; // at target rate - RealTime latency_t = RealTime::frame2RealTime(latency, targetRate); - size_t stretchlat = 0; double timeRatio = 1.0; @@ -583,8 +593,6 @@ } bool looping = m_viewManager->getPlayLoopMode(); - bool constrained = (m_viewManager->getPlaySelectionMode() && - !m_viewManager->getSelections().empty()); #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING std::cerr << "\nbuffered to: " << bufferedto_t << ", in buffer: " << inbuffer_t << ", time ratio " << timeRatio << "\n stretcher latency: " << stretchlat_t << ", device latency: " << latency_t << "\n since request: " << sincerequest_t << ", last retrieved: " << lastretrieved_t << std::endl; @@ -592,24 +600,109 @@ RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - MultiSelection::SelectionList::const_iterator i; + // Normally the range lists should contain at least one item each + // -- if playback is unconstrained, that item should report the + // entire source audio duration. - // These could be cached from one call to the next, if the - // selection has not changed... but some of the work would still - // need to be done because the playback model may have changed. + if (m_rangeStarts.empty()) { + rebuildRangeLists(); + } - // Currently, we know that this method is only ever called from a - // single thread (the GUI thread), so we could be nasty and - // maintain these as statics to avoid re-creating them... - - std::vector<RealTime> rangeStarts; - std::vector<RealTime> rangeDurations; + if (m_rangeStarts.empty()) { + // this code is only used in case of error in rebuildRangeLists + RealTime playing_t = bufferedto_t + - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t + + sincerequest_t; + size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); + return m_viewManager->alignPlaybackFrameToReference(frame); + } int inRange = 0; int index = 0; - if (constrained) { + for (size_t i = 0; i < m_rangeStarts.size(); ++i) { + if (bufferedto_t >= m_rangeStarts[i]) { + inRange = index; + } else { + break; + } + ++index; + } + + if (inRange >= m_rangeStarts.size()) inRange = m_rangeStarts.size()-1; + + RealTime playing_t = bufferedto_t - m_rangeStarts[inRange]; + + playing_t = playing_t + - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t + + sincerequest_t; + +#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING + std::cerr << "playing_t as offset into range " << inRange << " (with start = " << m_rangeStarts[inRange] << ") = " << playing_t << std::endl; +#endif + + while (playing_t < RealTime::zeroTime) { + + if (inRange == 0) { + if (looping) { + inRange = m_rangeStarts.size() - 1; + } else { + break; + } + } else { + --inRange; + } + + playing_t = playing_t + m_rangeDurations[inRange]; + } + + playing_t = playing_t + m_rangeStarts[inRange]; + +#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING + std::cerr << " playing time: " << playing_t << std::endl; +#endif + + if (!looping) { + if (inRange == m_rangeStarts.size()-1 && + playing_t >= m_rangeStarts[inRange] + m_rangeDurations[inRange]) { + stop(); + } + } + + if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; + + size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); + return m_viewManager->alignPlaybackFrameToReference(frame); +} + +void +AudioCallbackPlaySource::rebuildRangeLists() +{ + bool constrained = (m_viewManager->getPlaySelectionMode()); + + m_rangeStarts.clear(); + m_rangeDurations.clear(); + + size_t sourceRate = getSourceSampleRate(); + if (sourceRate == 0) return; + + RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate); + if (end == RealTime::zeroTime) return; + + if (!constrained) { + m_rangeStarts.push_back(RealTime::zeroTime); + m_rangeDurations.push_back(end); + return; + } + + MultiSelection::SelectionList selections = m_viewManager->getSelections(); + MultiSelection::SelectionList::const_iterator i; + +#ifdef DEBUG_AUDIO_PLAY_SOURCE + std::cerr << "AudioCallbackPlaySource::rebuildRangeLists" << std::endl; +#endif + + if (!selections.empty()) { for (i = selections.begin(); i != selections.end(); ++i) { @@ -623,66 +716,17 @@ m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()), sourceRate)); - rangeStarts.push_back(start); - rangeDurations.push_back(duration); - - if (bufferedto_t >= start) { - inRange = index; - } - - ++index; + m_rangeStarts.push_back(start); + m_rangeDurations.push_back(duration); } + } else { + m_rangeStarts.push_back(RealTime::zeroTime); + m_rangeDurations.push_back(end); } - if (rangeStarts.empty()) { - rangeStarts.push_back(RealTime::zeroTime); - rangeDurations.push_back(end); - } - - if (inRange >= rangeStarts.size()) inRange = rangeStarts.size()-1; - - RealTime playing_t = bufferedto_t - rangeStarts[inRange]; - - playing_t = playing_t - - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t - + sincerequest_t; - -#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING - std::cerr << "playing_t as offset into range " << inRange << " (with start = " << rangeStarts[inRange] << ") = " << playing_t << std::endl; +#ifdef DEBUG_AUDIO_PLAY_SOURCE + std::cerr << "Now have " << m_rangeStarts.size() << " play ranges" << std::endl; #endif - - while (playing_t < RealTime::zeroTime) { - - if (inRange == 0) { - if (looping) { - inRange = rangeStarts.size() - 1; - } else { - break; - } - } else { - --inRange; - } - - playing_t = playing_t + rangeDurations[inRange]; - } - - playing_t = playing_t + rangeStarts[inRange]; - -#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING - std::cerr << " playing time: " << playing_t << std::endl; -#endif - - if (!looping) { - if (inRange == rangeStarts.size()-1 && - playing_t >= rangeStarts[inRange] + rangeDurations[inRange]) { - stop(); - } - } - - if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; - - size_t frame = RealTime::realTime2Frame(playing_t, sourceRate); - return m_viewManager->alignPlaybackFrameToReference(frame); } void
--- a/audioio/AudioCallbackPlaySource.h Fri Feb 08 17:54:49 2008 +0000 +++ b/audioio/AudioCallbackPlaySource.h Mon Feb 11 12:46:39 2008 +0000 @@ -26,6 +26,7 @@ #include <QWaitCondition> #include "base/Thread.h" +#include "base/RealTime.h" #include <samplerate.h> @@ -99,6 +100,12 @@ * out of the speakers. (i.e. compensating for playback latency.) */ virtual size_t getCurrentPlayingFrame(); + + /** + * Return the last frame that would come out of the speakers if we + * stopped playback right now. + */ + virtual size_t getCurrentBufferedFrame(); /** * Return the frame at which playback is expected to end (if not looping). @@ -331,6 +338,13 @@ // Called from getSourceSamples. void applyAuditioningEffect(size_t count, float **buffers); + // Ranges of current selections, if play selection is active + std::vector<RealTime> m_rangeStarts; + std::vector<RealTime> m_rangeDurations; + void rebuildRangeLists(); + + size_t getCurrentFrame(RealTime outputLatency); + class FillThread : public Thread { public:
--- a/framework/MainWindowBase.cpp Fri Feb 08 17:54:49 2008 +0000 +++ b/framework/MainWindowBase.cpp Mon Feb 11 12:46:39 2008 +0000 @@ -449,9 +449,18 @@ } Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); - int frame = m_playSource->getCurrentPlayingFrame(); - std::cerr << "playing frame (in ref model) = " << frame << std::endl; + // What we want here is not the currently playing frame (unless we + // are about to clear out the audio playback buffers -- which may + // or may not be possible, depending on the audio driver). What + // we want is the frame that was last committed to the soundcard + // buffers, as the audio driver will continue playing up to that + // frame before switching to whichever one we decide we want to + // switch to, regardless of our efforts. + + int frame = m_playSource->getCurrentBufferedFrame(); + +// std::cerr << "currentPaneChanged: current frame (in ref model) = " << frame << std::endl; View::ModelSet soloModels = p->getModels(); @@ -488,11 +497,6 @@ m_playSource->setSoloModelSet(soloModels); if (a && b && (a != b)) { -/*!!! - int rframe = a->alignToReference(frame); - int bframe = b->alignFromReference(rframe); - if (m_playSource->isPlaying()) m_playSource->play(bframe); -*/ if (m_playSource->isPlaying()) m_playSource->play(frame); } }