diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 8eb94eefc..102a1ba88 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -2277,7 +2277,12 @@ int AudioIO::StartStream(const TransportTracks &tracks, while( mAudioThreadShouldCallFillBuffersOnce ) { if (mScrubQueue) - mScrubQueue->Nudge(); + // If not using a scrub thread, then we need to let the mouse polling + // in the main thread deliver its messages to ScrubQueue, so that + // FillBuffers() can progress in priming the RingBuffer. Else we would + // deadlock here. If not using the scrub thread, this should be + // harmless. + wxEventLoopBase::GetActive()->YieldFor( wxEVT_CATEGORY_TIMER ); wxMilliSleep( 50 ); } diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index bcd3c7d26..216ef0e01 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -336,8 +336,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) double time1 = std::min(maxTime, viewInfo.PositionToTime(position, leftOffset) ); - if (time1 != time0) - { + if (time1 != time0) { if (busy) { auto position = mScrubStartPosition; ctb->StopPlaying(); @@ -404,6 +403,15 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) ); #endif mScrubSpeedDisplayCountdown = 0; + + // Must start the thread and poller first or else PlayPlayRegion + // will deadlock + StartPolling(); + auto cleanup = finally([this]{ + if (mScrubToken < 0) + StopPolling(); + }); + mScrubToken = ctb->PlayPlayRegion(SelectedRegion(time0, time1), options, PlayMode::normalPlay, appearance, backwards); @@ -422,17 +430,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx) mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis(); if (IsScrubbing()) { - mPaused = false; mLastScrubPosition = xx; - -#ifdef USE_SCRUB_THREAD - // Detached thread is self-deleting, after it receives the Delete() message - mpThread = safenew ScrubPollerThread{ *this }; - mpThread->Create(4096); - mpThread->Run(); -#endif - - mPoller->Start(ScrubPollInterval_ms); } // Return true whether we started scrub, or are still waiting to decide. @@ -492,6 +490,14 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1) ); #endif + // Must start the thread and poller first or else PlayPlayRegion + // will deadlock + StartPolling(); + auto cleanup = finally([this]{ + if (mScrubToken < 0) + StopPolling(); + }); + mScrubSpeedDisplayCountdown = 0; // last buffer should not be any bigger than this. double lastBuffer = (2 * ScrubPollInterval_ms) / 1000.0; @@ -501,18 +507,9 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1) PlayMode::normalPlay, appearance, backwards); if (mScrubToken >= 0) { - mPaused = false; mLastScrubPosition = 0; - -#ifdef USE_SCRUB_THREAD - // Detached thread is self-deleting, after it receives the Delete() message - mpThread = safenew ScrubPollerThread{ *this }; - mpThread->Create(4096); - mpThread->Run(); -#endif - - mPoller->Start(ScrubPollInterval_ms); } + return true; } @@ -631,16 +628,37 @@ void Scrubber::ContinueScrubbingUI() } } -void Scrubber::StopScrubbing() +void Scrubber::StartPolling() { + mPaused = false; + +#ifdef USE_SCRUB_THREAD + // Detached thread is self-deleting, after it receives the Delete() message + mpThread = safenew ScrubPollerThread{ *this }; + mpThread->Create(4096); + mpThread->Run(); +#endif + + mPoller->Start(ScrubPollInterval_ms); +} + +void Scrubber::StopPolling() +{ + mPaused = true; + #ifdef USE_SCRUB_THREAD if (mpThread) { mpThread->Delete(); mpThread = nullptr; } #endif - + mPoller->Stop(); +} + +void Scrubber::StopScrubbing() +{ + StopPolling(); if (HasMark() && !mCancelled) { const wxMouseState state(::wxGetMouseState()); diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index 30fcf9db9..37f73e75d 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -154,6 +154,8 @@ public: void CheckMenuItems(); private: + void StartPolling(); + void StopPolling(); void DoScrub(bool seek); void OnActivateOrDeactivateApp(wxActivateEvent & event);