From f0637eeb85649a90a365d572f7f179bb17f855a6 Mon Sep 17 00:00:00 2001 From: James Crook Date: Sat, 21 Jul 2018 21:14:30 +0100 Subject: [PATCH] Bug 1906 - Issues with dynamic Play-at-Speed (Play doesn't pop up) Fixed issue 1, i.e. the play button does not pop up after play at speed completes. --- src/AudioIO.cpp | 61 ++++++++++++++++++++++++++----------- src/AudioIO.h | 1 + src/tracks/ui/Scrubbing.cpp | 8 +++-- src/tracks/ui/Scrubbing.h | 1 + 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 5d35f69d8..44f612f39 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -2003,7 +2003,7 @@ int AudioIO::StartStream(const TransportTracks &tracks, } else { playbackTime = lrint(scrubOptions.delay * sampleRate) / sampleRate; - mPlayMode = PLAY_SCRUB; + mPlayMode = (scrubOptions.isPlayingAtSpeed) ? PLAY_AT_SPEED : PLAY_SCRUB; } } #endif @@ -2942,7 +2942,7 @@ double AudioIO::NormalizeStreamTime(double absoluteTime) const #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT // Limit the time between t0 and t1 if not scrubbing. // Should the limiting be necessary in any play mode if there are no bugs? - if (mPlayMode != PLAY_SCRUB) + if( (mPlayMode != PLAY_SCRUB) && (mPlayMode != PLAY_AT_SPEED)) #endif absoluteTime = LimitStreamTime(absoluteTime); @@ -3262,6 +3262,11 @@ AudioThread::ExitCode AudioThread::Entry() // This allows the scrubbing update interval to be made very short without // playback becoming intermittent. } + else if (gAudioIO->mPlayMode == AudioIO::PLAY_AT_SPEED) { + // Rely on the Wait() in ScrubQueue::Transformer() + // This allows the scrubbing update interval to be made very short without + // playback becoming intermittent. + } else { // Perhaps this too could use a condition variable, for available space in the // ring buffer, instead of a polling loop? But no harm in doing it this way. @@ -3850,7 +3855,7 @@ void AudioIO::FillBuffers() auto frames = available; bool progress = true; #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT - if (mPlayMode == PLAY_SCRUB) + if ((mPlayMode == PLAY_SCRUB) || (mPlayMode == PLAY_AT_SPEED)) // scrubbing does not use warped time and length frames = limitSampleBufferSize(frames, mScrubDuration); else @@ -3887,7 +3892,8 @@ void AudioIO::FillBuffers() // don't generate either if scrubbing at zero speed. #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT - const bool silent = (mPlayMode == PLAY_SCRUB) && mSilentScrub; + const bool silent = ((mPlayMode == PLAY_SCRUB)|| + (mPlayMode == PLAY_AT_SPEED)) && mSilentScrub; #else const bool silent = false; #endif @@ -3930,6 +3936,7 @@ void AudioIO::FillBuffers() { #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT case PLAY_SCRUB: + case PLAY_AT_SPEED: { mScrubDuration -= frames; wxASSERT(mScrubDuration >= 0); @@ -4940,6 +4947,8 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, // While scrubbing, ignore seek requests if (gAudioIO->mSeek && gAudioIO->mPlayMode == AudioIO::PLAY_SCRUB) gAudioIO->mSeek = 0.0; + else if (gAudioIO->mSeek && gAudioIO->mPlayMode == AudioIO::PLAY_AT_SPEED) + gAudioIO->mSeek = 0.0; else #endif if (gAudioIO->mSeek) @@ -5118,22 +5127,37 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, } group++; - // If our buffer is empty and the time indicator is past - // the end, then we've actually finished playing the entire + + bool bDone = true; + + // If the time indicator is past + // the end, then we may have finished playing the entire // selection. - // msmeyer: We never finish if we are playing looped - // PRL: or scrubbing. - if (len == 0 && - gAudioIO->mPlayMode == AudioIO::PLAY_STRAIGHT) { - if ((gAudioIO->ReversedTime() + if (bDone) + bDone = bDone && (gAudioIO->ReversedTime() ? gAudioIO->mTime <= gAudioIO->mT1 - : gAudioIO->mTime >= gAudioIO->mT1)) - // PRL: singalling MIDI output complete is necessary if - // not USE_MIDI_THREAD, otherwise it's harmlessly redundant + : gAudioIO->mTime >= gAudioIO->mT1); + + // We never finish if we are playing looped or or scrubbing. + if (bDone) { + // playing straight we must have no more audio. + if (gAudioIO->mPlayMode == AudioIO::PLAY_STRAIGHT) + bDone = (len == 0); + // playing at speed, it is OK to have some audio left over. + else if (gAudioIO->mPlayMode == AudioIO::PLAY_AT_SPEED) + bDone = true; + else + bDone = false; + } + + + if (bDone){ + // PRL: singalling MIDI output complete is necessary if + // not USE_MIDI_THREAD, otherwise it's harmlessly redundant #ifdef EXPERIMENTAL_MIDI_OUT - gAudioIO->mMidiOutputComplete = true, + gAudioIO->mMidiOutputComplete = true, #endif - callbackReturn = paComplete; + callbackReturn = paComplete; } if (cut) // no samples to process, they've been discarded @@ -5208,6 +5232,8 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, // be less than framesPerBuffer (during "stutter") if (gAudioIO->mPlayMode == AudioIO::PLAY_SCRUB) gAudioIO->mTime = gAudioIO->mScrubQueue->Consumer(maxLen); + else if (gAudioIO->mPlayMode == AudioIO::PLAY_AT_SPEED) + gAudioIO->mTime = gAudioIO->mScrubQueue->Consumer(maxLen); #endif em.RealtimeProcessEnd(); @@ -5344,7 +5370,8 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, // Update the current time position if not scrubbing // (Already did it above, for scrubbing) #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT - if (gAudioIO->mPlayMode != AudioIO::PLAY_SCRUB) + if( (gAudioIO->mPlayMode != AudioIO::PLAY_SCRUB) && + (gAudioIO->mPlayMode != AudioIO::PLAY_AT_SPEED) ) #endif { double delta = framesPerBuffer / gAudioIO->mRate; diff --git a/src/AudioIO.h b/src/AudioIO.h index 957b91218..6e8617f28 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -750,6 +750,7 @@ private: PLAY_LOOPED, #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT PLAY_SCRUB, + PLAY_AT_SPEED, // a version of PLAY_SCRUB. #endif } mPlayMode; double mCutPreviewGapStart; diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index f04ce5aaa..3312cc30f 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -354,6 +354,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) options.pScrubbingOptions = &mOptions; options.timeTrack = NULL; mOptions.delay = (ScrubPollInterval_ms * 0.9 / 1000.0); + mOptions.isPlayingAtSpeed = false; mOptions.minSpeed = 0.0; #ifdef USE_TRANSCRIPTION_TOOLBAR if (!mAlwaysSeeking) { @@ -472,7 +473,7 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1) mOptions.minStutter = lrint(std::max(0.0, MinStutter) * options.rate); mOptions.enqueueBySpeed = true; mOptions.adjustStart = false; - + mOptions.isPlayingAtSpeed = true; ControlToolBar::PlayAppearance appearance = ControlToolBar::PlayAppearance::Straight; @@ -486,8 +487,11 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1) #endif mScrubSpeedDisplayCountdown = 0; + // last buffer should not be any bigger than this. + double lastBuffer = (2 * ScrubPollInterval_ms) / 1000.0; mScrubToken = - ctb->PlayPlayRegion(SelectedRegion(time0, time1), options, + // Reduce time by 'lastBuffer' fudge factor, so that the Play will stop. + ctb->PlayPlayRegion(SelectedRegion(time0, time1-lastBuffer), options, PlayMode::normalPlay, appearance, backwards); if (mScrubToken >= 0) { diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index a17635489..2d45ef44d 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -45,6 +45,7 @@ struct ScrubbingOptions { sampleCount minSample {}; bool enqueueBySpeed {}; + bool isPlayingAtSpeed{}; double delay {};