1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-09-23 15:41:09 +02:00

Fix the hiccup at the start of scrub or play at speed...

... We need to start the polling of mouse state before starting the audio
stream, and not "nudge" AudioThread, so that AudioThread primes the ring
buffer correctly, not inserting some silence.

This requires yields to timer events in AudioIO::StartStream.
This commit is contained in:
Paul Licameli 2018-08-15 23:10:25 -04:00
parent 6fa5c93b8e
commit 26f72b110c
3 changed files with 50 additions and 25 deletions

View File

@ -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 );
}

View File

@ -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,8 +628,24 @@ 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();
@ -641,6 +654,11 @@ void Scrubber::StopScrubbing()
#endif
mPoller->Stop();
}
void Scrubber::StopScrubbing()
{
StopPolling();
if (HasMark() && !mCancelled) {
const wxMouseState state(::wxGetMouseState());

View File

@ -154,6 +154,8 @@ public:
void CheckMenuItems();
private:
void StartPolling();
void StopPolling();
void DoScrub(bool seek);
void OnActivateOrDeactivateApp(wxActivateEvent & event);