mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-02 17:09:26 +02:00
Changes to the starting and stopping of scrub/seek/at-speed...
... Necessary but not sufficient for fixing the "bounce" at start of scrub, and for simplifying thread synchronization
This commit is contained in:
commit
976e9aeec7
@ -549,15 +549,6 @@ struct AudioIO::ScrubState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is for avoiding deadlocks while starting a scrub:
|
|
||||||
// Audio stream needs to be unblocked
|
|
||||||
void Nudge()
|
|
||||||
{
|
|
||||||
wxMutexLocker locker(mUpdating);
|
|
||||||
mNudged = true;
|
|
||||||
mAvailable.Signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Update(double end, const ScrubbingOptions &options)
|
bool Update(double end, const ScrubbingOptions &options)
|
||||||
{
|
{
|
||||||
// Main thread indicates a scrubbing interval
|
// Main thread indicates a scrubbing interval
|
||||||
@ -617,14 +608,14 @@ struct AudioIO::ScrubState
|
|||||||
if (!cleanup) {
|
if (!cleanup) {
|
||||||
cleanup.create(mUpdating);
|
cleanup.create(mUpdating);
|
||||||
}
|
}
|
||||||
while(!mNudged && mMiddleIdx == mLeadingIdx)
|
while(! mStopped.load( std::memory_order_relaxed )&&
|
||||||
|
mMiddleIdx == mLeadingIdx)
|
||||||
mAvailable.Wait();
|
mAvailable.Wait();
|
||||||
|
|
||||||
mNudged = false;
|
|
||||||
|
|
||||||
auto now = ::wxGetLocalTimeMillis();
|
auto now = ::wxGetLocalTimeMillis();
|
||||||
|
|
||||||
if (mMiddleIdx != mLeadingIdx) {
|
if ( ! mStopped.load( std::memory_order_relaxed ) &&
|
||||||
|
mMiddleIdx != mLeadingIdx ) {
|
||||||
Data &entry = mEntries[mMiddleIdx];
|
Data &entry = mEntries[mMiddleIdx];
|
||||||
if (entry.mDuration > 0) {
|
if (entry.mDuration > 0) {
|
||||||
// First use of the entry
|
// First use of the entry
|
||||||
@ -646,11 +637,18 @@ struct AudioIO::ScrubState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We got the shut-down signal, or we got nudged, or we discarded all the work.
|
// We got the shut-down signal, or we discarded all the work.
|
||||||
startSample = endSample = duration = -1L;
|
startSample = endSample = duration = -1L;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Stop()
|
||||||
|
{
|
||||||
|
mStopped.store( true, std::memory_order_relaxed );
|
||||||
|
wxMutexLocker locker(mUpdating);
|
||||||
|
mAvailable.Signal();
|
||||||
|
}
|
||||||
|
|
||||||
double LastTrackTime() const
|
double LastTrackTime() const
|
||||||
{
|
{
|
||||||
// Needed by the main thread sometimes
|
// Needed by the main thread sometimes
|
||||||
@ -832,12 +830,12 @@ private:
|
|||||||
unsigned mTrailingIdx;
|
unsigned mTrailingIdx;
|
||||||
unsigned mMiddleIdx;
|
unsigned mMiddleIdx;
|
||||||
unsigned mLeadingIdx;
|
unsigned mLeadingIdx;
|
||||||
|
std::atomic<bool> mStopped { false };
|
||||||
const double mRate;
|
const double mRate;
|
||||||
wxLongLong mLastScrubTimeMillis;
|
wxLongLong mLastScrubTimeMillis;
|
||||||
|
|
||||||
mutable wxMutex mUpdating;
|
mutable wxMutex mUpdating;
|
||||||
mutable wxCondition mAvailable { mUpdating };
|
mutable wxCondition mAvailable { mUpdating };
|
||||||
bool mNudged { false };
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -2006,9 +2004,17 @@ int AudioIO::StartStream(const TransportTracks &tracks,
|
|||||||
mAudioThreadShouldCallFillBuffersOnce = true;
|
mAudioThreadShouldCallFillBuffersOnce = true;
|
||||||
|
|
||||||
while( mAudioThreadShouldCallFillBuffersOnce ) {
|
while( mAudioThreadShouldCallFillBuffersOnce ) {
|
||||||
if (mScrubState)
|
#ifndef USE_SCRUB_THREAD
|
||||||
mScrubState->Nudge();
|
// Yuck, we either have to poll "by hand" when scrub polling doesn't
|
||||||
wxMilliSleep( 50 );
|
// work with a thread, or else yield to timer messages, but that would
|
||||||
|
// execute too much else
|
||||||
|
if (mScrubState) {
|
||||||
|
mOwningProject->GetScrubber().ContinueScrubbingPoll();
|
||||||
|
wxMilliSleep( Scrubber::ScrubPollInterval_ms );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
wxMilliSleep( 50 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mNumPlaybackChannels > 0 || mNumCaptureChannels > 0) {
|
if(mNumPlaybackChannels > 0 || mNumCaptureChannels > 0) {
|
||||||
@ -2513,8 +2519,6 @@ void AudioIO::StopStream()
|
|||||||
//
|
//
|
||||||
|
|
||||||
mAudioThreadFillBuffersLoopRunning = false;
|
mAudioThreadFillBuffersLoopRunning = false;
|
||||||
if (mScrubState)
|
|
||||||
mScrubState->Nudge();
|
|
||||||
|
|
||||||
// Audacity can deadlock if it tries to update meters while
|
// Audacity can deadlock if it tries to update meters while
|
||||||
// we're stopping PortAudio (because the meter updating code
|
// we're stopping PortAudio (because the meter updating code
|
||||||
@ -2622,8 +2626,6 @@ void AudioIO::StopStream()
|
|||||||
{
|
{
|
||||||
// LLL: Experienced recursive yield here...once.
|
// LLL: Experienced recursive yield here...once.
|
||||||
wxGetApp().Yield(true); // Pass true for onlyIfNeeded to avoid recursive call error.
|
wxGetApp().Yield(true); // Pass true for onlyIfNeeded to avoid recursive call error.
|
||||||
if (mScrubState)
|
|
||||||
mScrubState->Nudge();
|
|
||||||
wxMilliSleep( 50 );
|
wxMilliSleep( 50 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2768,6 +2770,12 @@ bool AudioIO::UpdateScrub
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioIO::StopScrub()
|
||||||
|
{
|
||||||
|
if (mScrubState)
|
||||||
|
mScrubState->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
double AudioIO::GetLastScrubTime() const
|
double AudioIO::GetLastScrubTime() const
|
||||||
{
|
{
|
||||||
if (mScrubState)
|
if (mScrubState)
|
||||||
|
@ -261,6 +261,8 @@ class AUDACITY_DLL_API AudioIO final {
|
|||||||
*/
|
*/
|
||||||
bool UpdateScrub(double endTimeOrSpeed, const ScrubbingOptions &options);
|
bool UpdateScrub(double endTimeOrSpeed, const ScrubbingOptions &options);
|
||||||
|
|
||||||
|
void StopScrub();
|
||||||
|
|
||||||
/** \brief return the ending time of the last scrub interval.
|
/** \brief return the ending time of the last scrub interval.
|
||||||
*/
|
*/
|
||||||
double GetLastScrubTime() const;
|
double GetLastScrubTime() const;
|
||||||
|
@ -51,9 +51,7 @@ enum {
|
|||||||
ScrubSpeedStepsPerOctave = 4,
|
ScrubSpeedStepsPerOctave = 4,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ScrubPollInterval_ms = 50,
|
kOneSecondCountdown = 1000 / Scrubber::ScrubPollInterval_ms,
|
||||||
|
|
||||||
kOneSecondCountdown = 1000 / ScrubPollInterval_ms,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const double MinStutter = 0.2;
|
static const double MinStutter = 0.2;
|
||||||
@ -336,8 +334,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
|
|||||||
double time1 = std::min(maxTime,
|
double time1 = std::min(maxTime,
|
||||||
viewInfo.PositionToTime(position, leftOffset)
|
viewInfo.PositionToTime(position, leftOffset)
|
||||||
);
|
);
|
||||||
if (time1 != time0)
|
if (time1 != time0) {
|
||||||
{
|
|
||||||
if (busy) {
|
if (busy) {
|
||||||
auto position = mScrubStartPosition;
|
auto position = mScrubStartPosition;
|
||||||
ctb->StopPlaying();
|
ctb->StopPlaying();
|
||||||
@ -403,6 +400,15 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
|
|||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
mScrubSpeedDisplayCountdown = 0;
|
mScrubSpeedDisplayCountdown = 0;
|
||||||
|
|
||||||
|
// Must start the thread and poller first or else PlayPlayRegion
|
||||||
|
// will insert some silence
|
||||||
|
StartPolling();
|
||||||
|
auto cleanup = finally([this]{
|
||||||
|
if (mScrubToken < 0)
|
||||||
|
StopPolling();
|
||||||
|
});
|
||||||
|
|
||||||
mScrubToken =
|
mScrubToken =
|
||||||
ctb->PlayPlayRegion(SelectedRegion(time0, time1), options,
|
ctb->PlayPlayRegion(SelectedRegion(time0, time1), options,
|
||||||
PlayMode::normalPlay, appearance, backwards);
|
PlayMode::normalPlay, appearance, backwards);
|
||||||
@ -421,17 +427,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
|
|||||||
mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis();
|
mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis();
|
||||||
|
|
||||||
if (IsScrubbing()) {
|
if (IsScrubbing()) {
|
||||||
mPaused = false;
|
|
||||||
mLastScrubPosition = xx;
|
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.
|
// Return true whether we started scrub, or are still waiting to decide.
|
||||||
@ -491,6 +487,14 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1)
|
|||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Must start the thread and poller first or else PlayPlayRegion
|
||||||
|
// will insert some silence
|
||||||
|
StartPolling();
|
||||||
|
auto cleanup = finally([this]{
|
||||||
|
if (mScrubToken < 0)
|
||||||
|
StopPolling();
|
||||||
|
});
|
||||||
|
|
||||||
mScrubSpeedDisplayCountdown = 0;
|
mScrubSpeedDisplayCountdown = 0;
|
||||||
// Aim to stop within 20 samples of correct position.
|
// Aim to stop within 20 samples of correct position.
|
||||||
double stopTolerance = 20.0 / options.rate;
|
double stopTolerance = 20.0 / options.rate;
|
||||||
@ -500,18 +504,9 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1)
|
|||||||
PlayMode::normalPlay, appearance, backwards);
|
PlayMode::normalPlay, appearance, backwards);
|
||||||
|
|
||||||
if (mScrubToken >= 0) {
|
if (mScrubToken >= 0) {
|
||||||
mPaused = false;
|
|
||||||
mLastScrubPosition = 0;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,8 +625,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
|
#ifdef USE_SCRUB_THREAD
|
||||||
if (mpThread) {
|
if (mpThread) {
|
||||||
mpThread->Delete();
|
mpThread->Delete();
|
||||||
@ -640,6 +651,12 @@ void Scrubber::StopScrubbing()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
mPoller->Stop();
|
mPoller->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scrubber::StopScrubbing()
|
||||||
|
{
|
||||||
|
gAudioIO->StopScrub();
|
||||||
|
StopPolling();
|
||||||
|
|
||||||
if (HasMark() && !mCancelled) {
|
if (HasMark() && !mCancelled) {
|
||||||
const wxMouseState state(::wxGetMouseState());
|
const wxMouseState state(::wxGetMouseState());
|
||||||
|
@ -72,6 +72,8 @@ struct ScrubbingOptions {
|
|||||||
class Scrubber : public wxEvtHandler
|
class Scrubber : public wxEvtHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static constexpr unsigned ScrubPollInterval_ms = 50;
|
||||||
|
|
||||||
Scrubber(AudacityProject *project);
|
Scrubber(AudacityProject *project);
|
||||||
~Scrubber();
|
~Scrubber();
|
||||||
|
|
||||||
@ -154,6 +156,8 @@ public:
|
|||||||
void CheckMenuItems();
|
void CheckMenuItems();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void StartPolling();
|
||||||
|
void StopPolling();
|
||||||
void DoScrub(bool seek);
|
void DoScrub(bool seek);
|
||||||
void OnActivateOrDeactivateApp(wxActivateEvent & event);
|
void OnActivateOrDeactivateApp(wxActivateEvent & event);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user