From 9727968b9420aa9bbcecc60d697629b4fc0a1663 Mon Sep 17 00:00:00 2001 From: Paul Licameli <paul.licameli@audacityteam.org> Date: Sun, 19 Aug 2018 11:16:46 -0400 Subject: [PATCH 1/4] move some functions --- src/AudioIO.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 800766d02..aa4aa7338 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -565,15 +565,6 @@ struct AudioIO::ScrubQueue dd.Cancel(); } } - ~ScrubQueue() {} - - double LastTimeInQueue() const - { - // Needed by the main thread sometimes - wxMutexLocker locker(mUpdating); - const Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size]; - return previous.mS1.as_double() / mRate; - } // This is for avoiding deadlocks while starting a scrub: // Audio stream needs to be unblocked @@ -735,6 +726,16 @@ struct AudioIO::ScrubQueue mLastTransformerTimeMillis = now; } + double LastTimeInQueue() const + { + // Needed by the main thread sometimes + wxMutexLocker locker(mUpdating); + const Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size]; + return previous.mS1.as_double() / mRate; + } + + ~ScrubQueue() {} + private: struct Entry { From 8badb0a9334859736b7208be0d1de239936a7b65 Mon Sep 17 00:00:00 2001 From: Paul Licameli <paul.licameli@audacityteam.org> Date: Sat, 18 Aug 2018 12:35:04 -0400 Subject: [PATCH 2/4] Rewrite the insertion of silences into scrub, combining queue entries --- src/AudioIO.cpp | 66 ++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index aa4aa7338..8da95dbda 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -592,18 +592,16 @@ struct AudioIO::ScrubQueue // Use the previous end as NEW start. const auto s0 = previous->mS1; Duration dd { *this }; - const auto &origDuration = dd.duration; - if (origDuration <= 0) + if (dd.duration <= 0) return false; - auto actualDuration = origDuration; const sampleCount s1 ( options.enqueueBySpeed ? s0.as_double() + - lrint(origDuration.as_double() * end) // end is a speed + lrint(dd.duration.as_double() * end) // end is a speed : lrint(end * mRate) // end is a time ); auto success = - current->Init(previous, s0, s1, actualDuration, options, mRate); + current->Init(previous, s0, s1, dd.duration, options, mRate); if (success) mLeadingIdx = next; else { @@ -611,21 +609,6 @@ struct AudioIO::ScrubQueue return false; } - // Fill up the queue with some silence if there was trimming - wxASSERT(actualDuration <= origDuration); - if (actualDuration < origDuration) { - next = (mLeadingIdx + 1) % Size; - if (next != mTrailingIdx) { - previous = &mEntries[(mLeadingIdx + Size - 1) % Size]; - current = &mEntries[mLeadingIdx]; - current->InitSilent(*previous, origDuration - actualDuration); - mLeadingIdx = next; - } - else - // Oops, can't enqueue the silence -- so do what? - ; - } - mAvailable.Signal(); return result; } @@ -710,12 +693,26 @@ struct AudioIO::ScrubQueue if (mMiddleIdx != mLeadingIdx) { // There is still work in the queue, after cancelling debt Entry &entry = mEntries[mMiddleIdx]; - startSample = entry.mS0; - endSample = entry.mS1; - duration = entry.mDuration; - mTrailingIdx = mMiddleIdx; - mMiddleIdx = (mMiddleIdx + 1) % Size; - mCredit += duration; + if (entry.mDuration > 0) { + // First use of the entry + startSample = entry.mS0; + endSample = entry.mS1; + duration = entry.mDuration; + entry.mDuration = 0; + mCredit += duration; + } + else if (entry.mSilence > 0) { + // Second use of the entry + startSample = endSample = entry.mS1; + duration = entry.mSilence; + entry.mSilence = 0; + mCredit += duration; + } + if (entry.mSilence == 0) { + // Entry is used up + mTrailingIdx = mMiddleIdx; + mMiddleIdx = (mMiddleIdx + 1) % Size; + } } else { // We got the shut-down signal, or we got nudged, or we discarded all the work. @@ -744,12 +741,16 @@ private: , mS1(0) , mGoal(0) , mDuration(0) + , mSilence(0) {} bool Init(Entry *previous, sampleCount s0, sampleCount s1, - sampleCount &duration /* in/out */, + sampleCount duration, const ScrubbingOptions &options, double rate) { + auto origDuration = duration; + mSilence = 0; + const bool &adjustStart = options.adjustStart; wxASSERT(duration > 0); @@ -867,14 +868,10 @@ private: mS0 = s0; mS1 = s1; mDuration = duration; - return true; - } + if (duration < origDuration) + mSilence = origDuration - duration; - void InitSilent(const Entry &previous, sampleCount duration) - { - mGoal = previous.mGoal; - mS0 = mS1 = previous.mS1; - mDuration = duration; + return true; } // These sample counts are initialized in the UI, producer, thread: @@ -885,6 +882,7 @@ private: // this work queue item corresponds to exactly this many samples of // playback output: sampleCount mDuration; + sampleCount mSilence; // The middleman Audio thread does not change these entries, but only // changes indices in the queue structure. From 4a98ba03dd67b1ceb1d744ccb21c18b82c969d9b Mon Sep 17 00:00:00 2001 From: Paul Licameli <paul.licameli@audacityteam.org> Date: Fri, 17 Aug 2018 13:13:41 -0400 Subject: [PATCH 3/4] Remove ScrubQueue compensation for Producer overflowing... ... which were meant to fix growth in lag between mouse movement and play head movement. Recent rewrites to keep RingBuffer more populated during scrub and seek should make this unnecessary. --- src/AudioIO.cpp | 65 ++----------------------------------------------- 1 file changed, 2 insertions(+), 63 deletions(-) diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 8da95dbda..57efe79e7 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -541,14 +541,13 @@ So a small, fixed queue size should be adequate. struct AudioIO::ScrubQueue { ScrubQueue(double t0, double t1, wxLongLong startClockMillis, - double rate, double maxDebt, + double rate, const ScrubbingOptions &options) : mTrailingIdx(0) , mMiddleIdx(1) , mLeadingIdx(1) , mRate(rate) , mLastScrubTimeMillis(startClockMillis) - , mMaxDebt { lrint(maxDebt * rate) } , mUpdating() { const sampleCount s0 { llrint( mRate * @@ -631,12 +630,8 @@ struct AudioIO::ScrubQueue // MAY ADVANCE mMiddleIdx, WHICH MAY EQUAL mLeadingIdx, BUT DOES NOT PASS IT. - bool checkDebt = false; if (!cleanup) { cleanup.create(mUpdating); - - // Check for cancellation of work only when re-enetering the cricial section - checkDebt = true; } while(!mNudged && mMiddleIdx == mLeadingIdx) mAvailable.Wait(); @@ -645,53 +640,7 @@ struct AudioIO::ScrubQueue auto now = ::wxGetLocalTimeMillis(); - if (checkDebt && - mLastTransformerTimeMillis >= 0 && // Not the first time for this scrub - mMiddleIdx != mLeadingIdx) { - // There is work in the queue, but if Producer is outrunning us, discard some, - // which may make a skip yet keep playback better synchronized with user gestures. - const auto interval = (now - mLastTransformerTimeMillis).ToDouble() / 1000.0; - //const Entry &previous = mEntries[(mMiddleIdx + Size - 1) % Size]; - const auto deficit = - static_cast<long>(interval * mRate) - // Samples needed in the last time interval - mCredit; // Samples done in the last time interval - mCredit = 0; - mDebt += deficit; - auto toDiscard = mDebt - mMaxDebt; - while (toDiscard > 0 && mMiddleIdx != mLeadingIdx) { - // Cancel some debt (discard some NEW work) - auto &entry = mEntries[mMiddleIdx]; - auto &dur = entry.mDuration; - if (toDiscard >= dur) { - // Discard entire queue entry - mDebt -= dur; - toDiscard -= dur; - dur = 0; - mTrailingIdx = mMiddleIdx; - mMiddleIdx = (mMiddleIdx + 1) % Size; - } - else { - // Adjust the start time - auto &start = entry.mS0; - const auto end = entry.mS1; - const auto ratio = toDiscard.as_double() / dur.as_double(); - const sampleCount adjustment( - std::abs((end - start).as_long_long()) * ratio - ); - if (start <= end) - start += adjustment; - else - start -= adjustment; - - mDebt -= toDiscard; - dur -= toDiscard; - toDiscard = 0; - } - } - } - if (mMiddleIdx != mLeadingIdx) { - // There is still work in the queue, after cancelling debt Entry &entry = mEntries[mMiddleIdx]; if (entry.mDuration > 0) { // First use of the entry @@ -699,14 +648,12 @@ struct AudioIO::ScrubQueue endSample = entry.mS1; duration = entry.mDuration; entry.mDuration = 0; - mCredit += duration; } else if (entry.mSilence > 0) { // Second use of the entry startSample = endSample = entry.mS1; duration = entry.mSilence; entry.mSilence = 0; - mCredit += duration; } if (entry.mSilence == 0) { // Entry is used up @@ -718,9 +665,6 @@ struct AudioIO::ScrubQueue // We got the shut-down signal, or we got nudged, or we discarded all the work. startSample = endSample = duration = -1L; } - - if (checkDebt) - mLastTransformerTimeMillis = now; } double LastTimeInQueue() const @@ -914,11 +858,6 @@ private: const double mRate; wxLongLong mLastScrubTimeMillis; - wxLongLong mLastTransformerTimeMillis { -1LL }; - sampleCount mCredit { 0 }; - sampleCount mDebt { 0 }; - const long mMaxDebt; - mutable wxMutex mUpdating; mutable wxCondition mAvailable { mUpdating }; bool mNudged { false }; @@ -2074,7 +2013,7 @@ int AudioIO::StartStream(const TransportTracks &tracks, std::make_unique<ScrubQueue>( mPlaybackSchedule.mT0, mPlaybackSchedule.mT1, scrubOptions.startClockTimeMillis, - mRate, 2 * scrubOptions.minStutterTime, + mRate, scrubOptions); mScrubDuration = 0; mSilentScrub = false; From 550d514e06ec0ce0e0a683bbae3d18572c067bf5 Mon Sep 17 00:00:00 2001 From: Paul Licameli <paul.licameli@audacityteam.org> Date: Wed, 15 Aug 2018 20:36:09 -0400 Subject: [PATCH 4/4] Changes of names and comments relating to scrub, don't mention queue --- src/AudioIO.cpp | 102 ++++++++++++++---------------------- src/AudioIO.h | 25 ++++----- src/tracks/ui/Scrubbing.cpp | 26 ++++----- src/tracks/ui/Scrubbing.h | 2 +- 4 files changed, 64 insertions(+), 91 deletions(-) diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 57efe79e7..d392e9fad 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -522,25 +522,9 @@ constexpr size_t TimeQueueGrainSize = 2000; #endif -/* -This work queue class coordinates two threads during scrub play: - -The UI thread which specifies scrubbing intervals to play, - -and the Audio thread which consumes those specifications -and fills the ring buffers with samples for play (to be consumed by yet another -thread, spawned by PortAudio). - -Audio produces samples for PortAudio, which consumes them, both in -approximate real time. The UI thread might go idle and so Audio -might catch up, emptying the queue and causing scrub to go silent. -The UI thread will not normally outrun Audio -- because InitEntry() -limits the real time duration over which each enqueued interval will play. -So a small, fixed queue size should be adequate. -*/ -struct AudioIO::ScrubQueue +struct AudioIO::ScrubState { - ScrubQueue(double t0, double t1, wxLongLong startClockMillis, + ScrubState(double t0, double t1, wxLongLong startClockMillis, double rate, const ScrubbingOptions &options) : mTrailingIdx(0) @@ -574,7 +558,7 @@ struct AudioIO::ScrubQueue mAvailable.Signal(); } - bool Producer(double end, const ScrubbingOptions &options) + bool Update(double end, const ScrubbingOptions &options) { // Main thread indicates a scrubbing interval @@ -594,7 +578,7 @@ struct AudioIO::ScrubQueue if (dd.duration <= 0) return false; - const sampleCount s1 ( options.enqueueBySpeed + const sampleCount s1 ( options.bySpeed ? s0.as_double() + lrint(dd.duration.as_double() * end) // end is a speed : lrint(end * mRate) // end is a time @@ -622,9 +606,9 @@ struct AudioIO::ScrubQueue } } - void Consumer(sampleCount &startSample, sampleCount &endSample, - sampleCount &duration, - Maybe<wxMutexLocker> &cleanup) + void Get(sampleCount &startSample, sampleCount &endSample, + sampleCount &duration, + Maybe<wxMutexLocker> &cleanup) { // Audio thread is ready for the next interval. @@ -641,7 +625,7 @@ struct AudioIO::ScrubQueue auto now = ::wxGetLocalTimeMillis(); if (mMiddleIdx != mLeadingIdx) { - Entry &entry = mEntries[mMiddleIdx]; + Data &entry = mEntries[mMiddleIdx]; if (entry.mDuration > 0) { // First use of the entry startSample = entry.mS0; @@ -667,20 +651,20 @@ struct AudioIO::ScrubQueue } } - double LastTimeInQueue() const + double LastTrackTime() const { // Needed by the main thread sometimes wxMutexLocker locker(mUpdating); - const Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size]; + const Data &previous = mEntries[(mLeadingIdx + Size - 1) % Size]; return previous.mS1.as_double() / mRate; } - ~ScrubQueue() {} + ~ScrubState() {} private: - struct Entry + struct Data { - Entry() + Data() : mS0(0) , mS1(0) , mGoal(0) @@ -688,7 +672,7 @@ private: , mSilence(0) {} - bool Init(Entry *previous, sampleCount s0, sampleCount s1, + bool Init(Data *previous, sampleCount s0, sampleCount s1, sampleCount duration, const ScrubbingOptions &options, double rate) { @@ -787,7 +771,7 @@ private: ) ); if (newDuration == 0) { - // Enqueue a silent scrub with s0 == s1 + // A silent scrub with s0 == s1 silent = true; s1 = s0; } @@ -818,22 +802,15 @@ private: return true; } - // These sample counts are initialized in the UI, producer, thread: sampleCount mS0; sampleCount mS1; sampleCount mGoal; - // This field is initialized in the UI thread too, and - // this work queue item corresponds to exactly this many samples of - // playback output: sampleCount mDuration; sampleCount mSilence; - - // The middleman Audio thread does not change these entries, but only - // changes indices in the queue structure. }; struct Duration { - Duration (ScrubQueue &queue_) : queue(queue_) {} + Duration (ScrubState &queue_) : queue(queue_) {} ~Duration () { if(!cancelled) @@ -842,7 +819,7 @@ private: void Cancel() { cancelled = true; } - ScrubQueue &queue; + ScrubState &queue; const wxLongLong clockTime { ::wxGetLocalTimeMillis() }; const sampleCount duration { static_cast<long long> (queue.mRate * (clockTime - queue.mLastScrubTimeMillis).ToDouble() / 1000.0) @@ -851,7 +828,7 @@ private: }; enum { Size = 10 }; - Entry mEntries[Size]; + Data mEntries[Size]; unsigned mTrailingIdx; unsigned mMiddleIdx; unsigned mLeadingIdx; @@ -1168,7 +1145,7 @@ AudioIO::AudioIO() mLastPlaybackTimeMillis = 0; #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT - mScrubQueue = NULL; + mScrubState = NULL; mScrubDuration = 0; mSilentScrub = false; #endif @@ -2009,8 +1986,8 @@ int AudioIO::StartStream(const TransportTracks &tracks, if (scrubbing) { const auto &scrubOptions = *options.pScrubbingOptions; - mScrubQueue = - std::make_unique<ScrubQueue>( + mScrubState = + std::make_unique<ScrubState>( mPlaybackSchedule.mT0, mPlaybackSchedule.mT1, scrubOptions.startClockTimeMillis, mRate, @@ -2019,7 +1996,7 @@ int AudioIO::StartStream(const TransportTracks &tracks, mSilentScrub = false; } else - mScrubQueue.reset(); + mScrubState.reset(); #endif // We signal the audio thread to call FillBuffers, to prime the RingBuffers @@ -2029,8 +2006,8 @@ int AudioIO::StartStream(const TransportTracks &tracks, mAudioThreadShouldCallFillBuffersOnce = true; while( mAudioThreadShouldCallFillBuffersOnce ) { - if (mScrubQueue) - mScrubQueue->Nudge(); + if (mScrubState) + mScrubState->Nudge(); wxMilliSleep( 50 ); } @@ -2321,7 +2298,7 @@ void AudioIO::StartStreamCleanup(bool bOnlyBuffers) } #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT - mScrubQueue.reset(); + mScrubState.reset(); #endif } @@ -2536,8 +2513,8 @@ void AudioIO::StopStream() // mAudioThreadFillBuffersLoopRunning = false; - if (mScrubQueue) - mScrubQueue->Nudge(); + if (mScrubState) + mScrubState->Nudge(); // Audacity can deadlock if it tries to update meters while // we're stopping PortAudio (because the meter updating code @@ -2645,8 +2622,8 @@ void AudioIO::StopStream() { // LLL: Experienced recursive yield here...once. wxGetApp().Yield(true); // Pass true for onlyIfNeeded to avoid recursive call error. - if (mScrubQueue) - mScrubQueue->Nudge(); + if (mScrubState) + mScrubState->Nudge(); wxMilliSleep( 50 ); } @@ -2747,7 +2724,7 @@ void AudioIO::StopStream() #endif #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT - mScrubQueue.reset(); + mScrubState.reset(); #endif if (mListener) { @@ -2782,19 +2759,19 @@ bool AudioIO::IsPaused() const } #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT -bool AudioIO::EnqueueScrub +bool AudioIO::UpdateScrub (double endTimeOrSpeed, const ScrubbingOptions &options) { - if (mScrubQueue) - return mScrubQueue->Producer(endTimeOrSpeed, options); + if (mScrubState) + return mScrubState->Update(endTimeOrSpeed, options); else return false; } -double AudioIO::GetLastTimeInScrubQueue() const +double AudioIO::GetLastScrubTime() const { - if (mScrubQueue) - return mScrubQueue->LastTimeInQueue(); + if (mScrubState) + return mScrubState->LastTrackTime(); else return -1.0; } @@ -3258,7 +3235,7 @@ AudioThread::ExitCode AudioThread::Entry() gAudioIO->mAudioThreadFillBuffersLoopActive = false; if (gAudioIO->mPlaybackSchedule.Interactive()) { - // Rely on the Wait() in ScrubQueue::Consumer() + // Rely on the Wait() in ScrubState::Consumer() // This allows the scrubbing update interval to be made very short without // playback becoming intermittent. } @@ -3861,7 +3838,7 @@ void AudioIO::FillBuffers() // times, to ensure, that the buffer has a reasonable size // This is the purpose of this loop. // PRL: or, when scrubbing, we may get work repeatedly from the - // scrub queue. + // user interface. bool done = false; Maybe<wxMutexLocker> cleanup; do { @@ -3948,7 +3925,8 @@ void AudioIO::FillBuffers() if (!done && mScrubDuration <= 0) { sampleCount startSample, endSample; - mScrubQueue->Consumer(startSample, endSample, mScrubDuration, cleanup); + mScrubState->Get( + startSample, endSample, mScrubDuration, cleanup); if (mScrubDuration < 0) { // Can't play anything diff --git a/src/AudioIO.h b/src/AudioIO.h index 46b96b446..33145598e 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -252,23 +252,18 @@ class AUDACITY_DLL_API AudioIO final { void SeekStream(double seconds) { mSeek = seconds; } #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT - bool IsScrubbing() const { return IsBusy() && mScrubQueue != 0; } + bool IsScrubbing() const { return IsBusy() && mScrubState != 0; } - /** \brief enqueue a NEW scrub play interval, using the last end as the NEW start, - * to be played over the same duration, as between this and the last - * enqueuing (or the starting of the stream). Except, we do not exceed maximum - * scrub speed, so may need to adjust either the start or the end. - * If options.adjustStart is true, then when mouse movement exceeds maximum scrub speed, - * adjust the beginning of the scrub interval rather than the end, so that - * the scrub skips or "stutters" to stay near the cursor. - * Return true if some sound was really enqueued. - * But if the "stutter" is too short for the minimum, enqueue nothing and return false. + /** \brief Notify scrubbing engine of desired position or speed. + * If options.adjustStart is true, then when mouse movement exceeds maximum + * scrub speed, adjust the beginning of the scrub interval rather than the + * end, so that the scrub skips or "stutters" to stay near the cursor. */ - bool EnqueueScrub(double endTimeOrSpeed, const ScrubbingOptions &options); + bool UpdateScrub(double endTimeOrSpeed, const ScrubbingOptions &options); - /** \brief return the ending time of the last enqueued scrub interval. + /** \brief return the ending time of the last scrub interval. */ - double GetLastTimeInScrubQueue() const; + double GetLastScrubTime() const; #endif /** \brief Returns true if audio i/o is busy starting, stopping, playing, @@ -803,8 +798,8 @@ private: wxMutex mSuspendAudioThread; #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT - struct ScrubQueue; - std::unique_ptr<ScrubQueue> mScrubQueue; + struct ScrubState; + std::unique_ptr<ScrubState> mScrubState; bool mSilentScrub; double mScrubSpeed; diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 187103a11..7ac90e0ff 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -476,7 +476,7 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1) mOptions.minTime = 0; mOptions.maxTime = time1; mOptions.minStutterTime = std::max(0.0, MinStutter); - mOptions.enqueueBySpeed = true; + mOptions.bySpeed = true; mOptions.adjustStart = false; mOptions.isPlayingAtSpeed = true; @@ -529,12 +529,12 @@ void Scrubber::ContinueScrubbingPoll() bool result = false; if (mPaused) { - // When paused, enqueue silent scrubs. + // When paused, make silent scrubs. mOptions.minSpeed = 0.0; mOptions.maxSpeed = mMaxSpeed; mOptions.adjustStart = false; - mOptions.enqueueBySpeed = true; - result = gAudioIO->EnqueueScrub(0, mOptions); + mOptions.bySpeed = true; + result = gAudioIO->UpdateScrub(0, mOptions); } else if (mSpeedPlaying) { // default speed of 1.3 set, so that we can hear there is a problem @@ -547,8 +547,8 @@ void Scrubber::ContinueScrubbingPoll() mOptions.minSpeed = speed -0.01; mOptions.maxSpeed = speed +0.01; mOptions.adjustStart = false; - mOptions.enqueueBySpeed = true; - result = gAudioIO->EnqueueScrub(speed, mOptions); + mOptions.bySpeed = true; + result = gAudioIO->UpdateScrub(speed, mOptions); } else { const wxMouseState state(::wxGetMouseState()); const auto trackPanel = mProject->GetTrackPanel(); @@ -556,14 +556,14 @@ void Scrubber::ContinueScrubbingPoll() const auto &viewInfo = mProject->GetViewInfo(); #ifdef DRAG_SCRUB if (mDragging && mSmoothScrollingScrub) { - const auto lastTime = gAudioIO->GetLastTimeInScrubQueue(); + const auto lastTime = gAudioIO->GetLastScrubTime(); const auto delta = mLastScrubPosition - position.x; const double time = viewInfo.OffsetTimeByPixels(lastTime, delta); mOptions.minSpeed = 0.0; mOptions.maxSpeed = mMaxSpeed; mOptions.adjustStart = true; - mOptions.enqueueBySpeed = false; - result = gAudioIO->EnqueueScrub(time, mOptions); + mOptions.bySpeed = false; + result = gAudioIO->UpdateScrub(time, mOptions); mLastScrubPosition = position.x; } else @@ -576,12 +576,12 @@ void Scrubber::ContinueScrubbingPoll() if (mSmoothScrollingScrub) { const double speed = FindScrubSpeed(seek, time); - mOptions.enqueueBySpeed = true; - result = gAudioIO->EnqueueScrub(speed, mOptions); + mOptions.bySpeed = true; + result = gAudioIO->UpdateScrub(speed, mOptions); } else { - mOptions.enqueueBySpeed = false; - result = gAudioIO->EnqueueScrub(time, mOptions); + mOptions.bySpeed = false; + result = gAudioIO->UpdateScrub(time, mOptions); } } } diff --git a/src/tracks/ui/Scrubbing.h b/src/tracks/ui/Scrubbing.h index 139494097..63268118e 100644 --- a/src/tracks/ui/Scrubbing.h +++ b/src/tracks/ui/Scrubbing.h @@ -44,7 +44,7 @@ struct ScrubbingOptions { double maxTime {}; double minTime {}; - bool enqueueBySpeed {}; + bool bySpeed {}; bool isPlayingAtSpeed{}; double delay {};