From 7d504ba015f23b3142740f762c4c69bed7e3295c Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 28 Jun 2019 11:57:49 -0400 Subject: [PATCH 1/8] ControlToolBar uses idle events to update status message for itself --- src/ProjectWindow.cpp | 1 - src/toolbars/ControlToolBar.cpp | 17 ++++++++--------- src/toolbars/ControlToolBar.h | 3 ++- src/tracks/ui/Scrubbing.cpp | 9 --------- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/ProjectWindow.cpp b/src/ProjectWindow.cpp index dbce707a0..1582f4fba 100644 --- a/src/ProjectWindow.cpp +++ b/src/ProjectWindow.cpp @@ -822,7 +822,6 @@ void ProjectWindow::Init() wxString msg = wxString::Format(_("Welcome to Audacity version %s"), AUDACITY_VERSION_STRING); statusBar->SetStatusText(msg, mainStatusBarField); - ControlToolBar::Get( project ).UpdateStatusBar( &project ); wxTheApp->Bind(EVT_THEME_CHANGE, &ProjectWindow::OnThemeChange, this); diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index a8b5a0ef6..d8861cb72 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -99,6 +99,7 @@ BEGIN_EVENT_TABLE(ControlToolBar, ToolBar) EVT_BUTTON(ID_REW_BUTTON, ControlToolBar::OnRewind) EVT_BUTTON(ID_FF_BUTTON, ControlToolBar::OnFF) EVT_BUTTON(ID_PAUSE_BUTTON, ControlToolBar::OnPause) + EVT_IDLE(ControlToolBar::OnIdle) END_EVENT_TABLE() //Standard constructor @@ -514,7 +515,6 @@ void ControlToolBar::SetPlay(bool down, PlayAppearance appearance) mPlay->SetAlternateIdx(0); } EnableDisableButtons(); - UpdateStatusBar( &mProject ); } void ControlToolBar::SetStop(bool down) @@ -810,7 +810,6 @@ void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt)) if (p) ProjectWindow::Get( *p ).TP_DisplaySelection(); - auto cleanup = finally( [&]{ UpdateStatusBar(p); } ); PlayDefault(); } @@ -818,7 +817,6 @@ void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt)) { if (CanStopAudioStream()) { StopPlaying(); - UpdateStatusBar( &mProject ); } } @@ -1104,9 +1102,6 @@ bool ControlToolBar::DoRecord(AudacityProject &project, SetStop(false); SetRecord(false); } - - // Success or not: - UpdateStatusBar( &mProject ); }); auto transportTracks = tracks; @@ -1319,8 +1314,12 @@ void ControlToolBar::OnPause(wxCommandEvent & WXUNUSED(evt)) { gAudioIO->SetPaused(mPaused); } +} - UpdateStatusBar( &mProject ); +void ControlToolBar::OnIdle(wxIdleEvent & event) +{ + event.Skip(); + UpdateStatusBar(); } void ControlToolBar::OnRewind(wxCommandEvent & WXUNUSED(evt)) @@ -1434,9 +1433,9 @@ wxString ControlToolBar::StateForStatusBar() return state; } -void ControlToolBar::UpdateStatusBar(AudacityProject *pProject) +void ControlToolBar::UpdateStatusBar() { - GetProjectFrame( *pProject ) + GetProjectFrame( mProject ) .GetStatusBar()->SetStatusText(StateForStatusBar(), stateStatusBarField); } diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index 710a1a4f6..47a4df78f 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -84,6 +84,7 @@ class ControlToolBar final : public ToolBar { const AudioIOStartStreamOptions &options); void OnFF(wxCommandEvent & evt); void OnPause(wxCommandEvent & evt); + void OnIdle(wxIdleEvent & event); // Choice among the appearances of the play button: enum class PlayAppearance { @@ -128,7 +129,6 @@ class ControlToolBar final : public ToolBar { void RegenerateTooltips() override; int WidthForStatusBar(wxStatusBar* const); - void UpdateStatusBar(AudacityProject *pProject); // Starting and stopping of scrolling display void StartScrollingIfPreferred(); @@ -141,6 +141,7 @@ class ControlToolBar final : public ToolBar { PlayMode GetLastPlayMode() const { return mLastPlayMode; } private: + void UpdateStatusBar(); static AButton *MakeButton( ControlToolBar *pBar, diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index 4de7ce16f..ed8c0f370 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -337,7 +337,6 @@ void Scrubber::MarkScrubStart( // : ControlToolBar::PlayAppearance::Scrub); mScrubStartPosition = xx; - ctb.UpdateStatusBar(mProject); mCancelled = false; } @@ -674,8 +673,6 @@ void Scrubber::ContinueScrubbingUI() // Show the correct status for seeking. bool backup = mSeeking; mSeeking = seek; - auto &ctb = ControlToolBar::Get( *mProject ); - ctb.UpdateStatusBar(mProject); mSeeking = backup; } @@ -1107,12 +1104,6 @@ void Scrubber::OnScrubOrSeek(bool seek) { DoScrub(seek); - if (HasMark()) { - // Show the correct status. - auto &ctb = ControlToolBar::Get( *mProject ); - ctb.UpdateStatusBar(mProject); - } - mSeeking = seek; CheckMenuItems(); From 8b549ea07ff57b9b60c76d42f5079e5ef6492d31 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 27 Jun 2019 23:01:53 -0400 Subject: [PATCH 2/8] Another overload of ControlToolBar::OnRecord taking bool... ... so that it's called directly where needed without the roundabout of SetInt in an event, and there is no checking of the state of a button first --- src/ProjectAudioManager.cpp | 5 +---- src/menus/TransportMenus.cpp | 5 +---- src/toolbars/ControlToolBar.cpp | 15 ++++++++------- src/toolbars/ControlToolBar.h | 1 + 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/ProjectAudioManager.cpp b/src/ProjectAudioManager.cpp index 346642f2e..f96bdfe91 100644 --- a/src/ProjectAudioManager.cpp +++ b/src/ProjectAudioManager.cpp @@ -330,11 +330,8 @@ void DoPause( AudacityProject &project ) void DoRecord( AudacityProject &project ) { - wxCommandEvent evt; - evt.SetInt(2); // 0 is default, use 1 to set shift on, 2 to clear it - auto &controlToolBar = ControlToolBar::Get( project ); - controlToolBar.OnRecord(evt); + controlToolBar.OnRecord(false); } void DoLockPlayRegion( AudacityProject &project ) diff --git a/src/menus/TransportMenus.cpp b/src/menus/TransportMenus.cpp index 2164f588f..adaff0849 100644 --- a/src/menus/TransportMenus.cpp +++ b/src/menus/TransportMenus.cpp @@ -247,11 +247,8 @@ void OnRecord(const CommandContext &context) void OnRecord2ndChoice(const CommandContext &context) { auto &project = context.project; - wxCommandEvent evt; - evt.SetInt(1); // 0 is default, use 1 to set shift on, 2 to clear it - auto &controlToolBar = ControlToolBar::Get( project ); - controlToolBar.OnRecord(evt); + controlToolBar.OnRecord(true); } void OnTimerRecord(const CommandContext &context) diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index d8861cb72..d2e2e636f 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -985,19 +985,20 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt) // Here instead we reduplicate some logic (from CommandHandler) because it isn't // normally used for buttons. - // Code from CommandHandler start... - AudacityProject *p = &mProject; - bool altAppearance = mRecord->WasShiftDown(); - if (evt.GetInt() == 1) // used when called by keyboard shortcut. Default (0) ignored. - altAppearance = true; - if (evt.GetInt() == 2) - altAppearance = false; + OnRecord( altAppearance ); +} +void ControlToolBar::OnRecord(bool altAppearance) +// STRONG-GUARANTEE (for state of current project's tracks) +{ bool bPreferNewTrack; gPrefs->Read("/GUI/PreferNewTrackRecord", &bPreferNewTrack, false); const bool appendRecord = (altAppearance == bPreferNewTrack); + // Code from CommandHandler start... + AudacityProject *p = &mProject; + if (p) { const auto &selectedRegion = ViewInfo::Get( *p ).selectedRegion; double t0 = selectedRegion.t0(); diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index 47a4df78f..015e7400b 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -77,6 +77,7 @@ class ControlToolBar final : public ToolBar { void OnPlay(wxCommandEvent & evt); void OnStop(wxCommandEvent & evt); void OnRecord(wxCommandEvent & evt); + void OnRecord(bool altAppearance); bool DoRecord(AudacityProject &project, const TransportTracks &transportTracks, // If captureTracks is empty, then tracks are created double t0, double t1, From 923128731d40695f2be72f3da1b4c630a5cc3b79 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 1 Jul 2019 10:19:09 -0400 Subject: [PATCH 3/8] Do not store pause state in ControlToolBar... ... Move that into ProjectAudioManager instead, and update the button, only to reflect it, in idle time. However, AudioIO also has its mPaused member variable, and it is not obvious that it was always kept the same as the button state. No attempt was made here to identify and fix any bugs, but only to preserve behavior. --- src/ProjectAudioManager.h | 6 +++++ src/toolbars/ControlToolBar.cpp | 46 ++++++++++++++------------------- src/toolbars/ControlToolBar.h | 4 --- src/tracks/ui/Scrubbing.cpp | 4 +-- 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/ProjectAudioManager.h b/src/ProjectAudioManager.h index 2d96e6a5c..df38ee134 100644 --- a/src/ProjectAudioManager.h +++ b/src/ProjectAudioManager.h @@ -35,6 +35,10 @@ public: void SetTimerRecordCancelled() { mTimerRecordCanceled = true; } void ResetTimerRecordCancelled() { mTimerRecordCanceled = false; } + bool Paused() const { return mPaused; } + + void SetPaused( bool value ) { mPaused = value; } + private: // Audio IO callback methods void OnAudioIORate(int rate) override; @@ -48,6 +52,8 @@ private: //flag for cancellation of timer record. bool mTimerRecordCanceled{ false }; + + bool mPaused{ false }; }; AudioIOStartStreamOptions DefaultPlayOptions( AudacityProject &project ); diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index d2e2e636f..0f87467d6 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -110,8 +110,6 @@ END_EVENT_TABLE() ControlToolBar::ControlToolBar( AudacityProject &project ) : ToolBar(project, TransportBarID, _("Transport"), wxT("Control")) { - mPaused = false; - gPrefs->Read(wxT("/GUI/ErgonomicTransportButtons"), &mErgonomicTransportButtons, true); mStrLocale = gPrefs->Read(wxT("/Locale/Language"), wxT("")); @@ -496,8 +494,8 @@ void ControlToolBar::EnableDisableButtons() !(playing && !paused) ); mStop->SetEnabled(CanStopAudioStream() && (playing || recording)); - mRewind->SetEnabled(IsPauseDown() || (!playing && !recording)); - mFF->SetEnabled(tracks && (IsPauseDown() || (!playing && !recording))); + mRewind->SetEnabled(paused || (!playing && !recording)); + mFF->SetEnabled(tracks && (paused || (!playing && !recording))); mPause->SetEnabled(CanStopAudioStream()); } @@ -544,11 +542,6 @@ void ControlToolBar::SetRecord(bool down, bool altAppearance) EnableDisableButtons(); } -bool ControlToolBar::IsPauseDown() const -{ - return mPause->IsDown(); -} - bool ControlToolBar::IsRecordDown() const { return mRecord->IsDown(); @@ -842,6 +835,7 @@ void ControlToolBar::StopPlaying(bool stopStream /* = true*/) StopScrolling(); AudacityProject *project = &mProject; + auto &projectAudioManager = ProjectAudioManager::Get( mProject ); if(project) { // Let scrubbing code do some appearance change @@ -866,10 +860,9 @@ void ControlToolBar::StopPlaying(bool stopStream /* = true*/) gAudioIO->AILADisable(); #endif - mPause->PopUp(); - mPaused=false; + projectAudioManager.SetPaused( false ); //Make sure you tell gAudioIO to unpause - gAudioIO->SetPaused(mPaused); + gAudioIO->SetPaused( false ); ClearCutPreviewTracks(); @@ -1273,21 +1266,14 @@ bool ControlToolBar::DoRecord(AudacityProject &project, void ControlToolBar::OnPause(wxCommandEvent & WXUNUSED(evt)) { + auto &projectAudioManager = ProjectAudioManager::Get( mProject ); + if (!CanStopAudioStream()) { return; } - - if(mPaused) - { - mPause->PopUp(); - mPaused=false; - } - else - { - mPause->PushDown(); - mPaused=true; - } + bool paused = !projectAudioManager.Paused(); + projectAudioManager.SetPaused( paused ); auto gAudioIO = AudioIO::Get(); @@ -1298,7 +1284,7 @@ void ControlToolBar::OnPause(wxCommandEvent & WXUNUSED(evt)) // Bug 1494 - Pausing a seek or scrub should just STOP as // it is confusing to be in a paused scrub state. - bool bStopInstead = mPaused && + bool bStopInstead = paused && gAudioIO->IsScrubbing() && !scrubber.IsSpeedPlaying(); @@ -1309,18 +1295,26 @@ void ControlToolBar::OnPause(wxCommandEvent & WXUNUSED(evt)) } if (gAudioIO->IsScrubbing()) - scrubber.Pause(mPaused); + scrubber.Pause(paused); else #endif { - gAudioIO->SetPaused(mPaused); + gAudioIO->SetPaused(paused); } } void ControlToolBar::OnIdle(wxIdleEvent & event) { event.Skip(); + + auto &projectAudioManager = ProjectAudioManager::Get( mProject ); + if ( projectAudioManager.Paused() ) + mPause->PushDown(); + else + mPause->PopUp(); + UpdateStatusBar(); + EnableDisableButtons(); } void ControlToolBar::OnRewind(wxCommandEvent & WXUNUSED(evt)) diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index 015e7400b..1f8a322f6 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -97,7 +97,6 @@ class ControlToolBar final : public ToolBar { void SetStop(bool down); void SetRecord(bool down, bool altAppearance = false); - bool IsPauseDown() const; bool IsRecordDown() const; // A project is only allowed to stop an audio stream that it owns. @@ -183,9 +182,6 @@ class ControlToolBar final : public ToolBar { static AudacityProject *mBusyProject; - // Maybe button state values shouldn't be duplicated in this toolbar? - bool mPaused; //Play or record is paused or not paused? - // Activate ergonomic order for transport buttons bool mErgonomicTransportButtons; diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index ed8c0f370..c018944c2 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -869,9 +869,7 @@ void Scrubber::OnActivateOrDeactivateApp(wxActivateEvent &event) // Pause if Pause down, or not scrubbing. if (!mProject) Pause(true); - else if ( !ControlToolBar::Find( *mProject ) ) - Pause( true ); - else if (ControlToolBar::Get( *mProject ).IsPauseDown()) + else if (ProjectAudioManager::Get( *mProject ).Paused()) Pause( true ); else if (!IsScrubbing()) Pause( true ); From cac04e9fb8b5ea72fa4161186278841d41377b83 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 1 Jul 2019 10:26:22 -0400 Subject: [PATCH 4/8] Do not use ControlToolBar's Record button as a state variable --- src/ProjectAudioManager.cpp | 9 ++++++ src/ProjectAudioManager.h | 9 ++++++ src/toolbars/ControlToolBar.cpp | 49 ++++++++++++--------------------- src/toolbars/ControlToolBar.h | 3 -- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/ProjectAudioManager.cpp b/src/ProjectAudioManager.cpp index f96bdfe91..a4a006326 100644 --- a/src/ProjectAudioManager.cpp +++ b/src/ProjectAudioManager.cpp @@ -192,6 +192,15 @@ void ProjectAudioManager::OnSoundActivationThreshold() } } +bool ProjectAudioManager::Recording() const +{ + auto gAudioIO = AudioIO::Get(); + return + gAudioIO->IsBusy() && + ControlToolBar::Get( mProject).CanStopAudioStream() && + gAudioIO->GetNumCaptureChannels() > 0; +} + AudioIOStartStreamOptions DefaultPlayOptions( AudacityProject &project ) { diff --git a/src/ProjectAudioManager.h b/src/ProjectAudioManager.h index df38ee134..d3a6cad4f 100644 --- a/src/ProjectAudioManager.h +++ b/src/ProjectAudioManager.h @@ -36,8 +36,16 @@ public: void ResetTimerRecordCancelled() { mTimerRecordCanceled = false; } bool Paused() const { return mPaused; } + + // Whether recording into this project (not just into some project) is + // active + bool Recording() const; + + // Whether the last attempt to start recording requested appending to tracks + bool Appending() const { return mAppending; } void SetPaused( bool value ) { mPaused = value; } + void SetAppending( bool value ) { mAppending = value; } private: // Audio IO callback methods @@ -54,6 +62,7 @@ private: bool mTimerRecordCanceled{ false }; bool mPaused{ false }; + bool mAppending{ false }; }; AudioIOStartStreamOptions DefaultPlayOptions( AudacityProject &project ); diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 0f87467d6..42a3209d9 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -455,7 +455,8 @@ void ControlToolBar::ReCreateButtons() if (recordDown) { - SetRecord(recordDown, recordShift); + mRecord->SetAlternateIdx(recordShift ? 1 : 0); + mRecord->PushDown(); } EnableDisableButtons(); @@ -527,26 +528,6 @@ void ControlToolBar::SetStop(bool down) EnableDisableButtons(); } -void ControlToolBar::SetRecord(bool down, bool altAppearance) -{ - if (down) - { - mRecord->SetAlternateIdx(altAppearance ? 1 : 0); - mRecord->PushDown(); - } - else - { - mRecord->SetAlternateIdx(0); - mRecord->PopUp(); - } - EnableDisableButtons(); -} - -bool ControlToolBar::IsRecordDown() const -{ - return mRecord->IsDown(); -} - int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode mode, @@ -593,7 +574,6 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, if (!success) { SetPlay(false); SetStop(false); - SetRecord(false); } } ); @@ -854,7 +834,6 @@ void ControlToolBar::StopPlaying(bool stopStream /* = true*/) if(stopStream) gAudioIO->StopStream(); SetPlay(false); - SetRecord(false); #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT gAudioIO->AILADisable(); @@ -1067,6 +1046,8 @@ bool ControlToolBar::DoRecord(AudacityProject &project, bool altAppearance, const AudioIOStartStreamOptions &options) { + auto &projectAudioManager = ProjectAudioManager::Get( mProject ); + CommandFlag flags = AlwaysEnabledFlag; // 0 means recalc flags. // NB: The call may have the side effect of changing flags. @@ -1079,22 +1060,16 @@ bool ControlToolBar::DoRecord(AudacityProject &project, // ...end of code from CommandHandler. auto gAudioIO = AudioIO::Get(); - if (gAudioIO->IsBusy()) { - if (!CanStopAudioStream() || 0 == gAudioIO->GetNumCaptureChannels()) - mRecord->PopUp(); - else - mRecord->PushDown(); + if (gAudioIO->IsBusy()) return false; - } - SetRecord(true, altAppearance); + projectAudioManager.SetAppending( !altAppearance ); bool success = false; auto cleanup = finally([&] { if (!success) { SetPlay(false); SetStop(false); - SetRecord(false); } }); @@ -1313,6 +1288,15 @@ void ControlToolBar::OnIdle(wxIdleEvent & event) else mPause->PopUp(); + if (!projectAudioManager.Recording()) { + mRecord->PopUp(); + mRecord->SetAlternateIdx( wxGetKeyState(WXK_SHIFT) ? 1 : 0 ); + } + else { + mRecord->PushDown(); + mRecord->SetAlternateIdx( projectAudioManager.Appending() ? 0 : 1 ); + } + UpdateStatusBar(); EnableDisableButtons(); } @@ -1403,6 +1387,7 @@ int ControlToolBar::WidthForStatusBar(wxStatusBar* const sb) wxString ControlToolBar::StateForStatusBar() { wxString state; + auto &projectAudioManager = ProjectAudioManager::Get( mProject ); auto pProject = &mProject; auto scrubState = pProject @@ -1412,7 +1397,7 @@ wxString ControlToolBar::StateForStatusBar() state = wxGetTranslation(scrubState); else if (mPlay->IsDown()) state = wxGetTranslation(mStatePlay); - else if (mRecord->IsDown()) + else if (projectAudioManager.Recording()) state = wxGetTranslation(mStateRecord); else state = wxGetTranslation(mStateStop); diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index 1f8a322f6..aae68dccd 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -95,9 +95,6 @@ class ControlToolBar final : public ToolBar { //These allow buttons to be controlled externally: void SetPlay(bool down, PlayAppearance appearance = PlayAppearance::Straight); void SetStop(bool down); - void SetRecord(bool down, bool altAppearance = false); - - bool IsRecordDown() const; // A project is only allowed to stop an audio stream that it owns. bool CanStopAudioStream () const; From 830f772625d32f448fee9b6b29237fec72f90ad5 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 1 Jul 2019 10:30:54 -0400 Subject: [PATCH 5/8] Update Play button appearance in idle time... ... so most calls to ControlToolBar::SetPlay are removed. One remains in TransportMenus, which will not be problematic for untangling dependencies, and one remains where the toolbar remakes its own buttons. But the routines that start and stop the streams, importantly, don't use it. --- src/ProjectAudioManager.cpp | 14 ++++++-- src/ProjectAudioManager.h | 10 +++++- src/menus/TransportMenus.cpp | 17 +++------- src/toolbars/ControlToolBar.cpp | 57 +++++++++++++++++++++------------ src/tracks/ui/Scrubbing.cpp | 9 ------ 5 files changed, 62 insertions(+), 45 deletions(-) diff --git a/src/ProjectAudioManager.cpp b/src/ProjectAudioManager.cpp index a4a006326..c80c3f661 100644 --- a/src/ProjectAudioManager.cpp +++ b/src/ProjectAudioManager.cpp @@ -192,6 +192,18 @@ void ProjectAudioManager::OnSoundActivationThreshold() } } +bool ProjectAudioManager::Playing() const +{ + auto gAudioIO = AudioIO::Get(); + return + gAudioIO->IsBusy() && + ControlToolBar::Get( mProject ).CanStopAudioStream() && + // ... and not merely monitoring + !gAudioIO->IsMonitoring() && + // ... and not punch-and-roll recording + gAudioIO->GetNumCaptureChannels() == 0; +} + bool ProjectAudioManager::Recording() const { auto gAudioIO = AudioIO::Get(); @@ -265,7 +277,6 @@ bool DoPlayStopSelect //If busy, stop playing, make sure everything is unpaused. if (scrubber.HasMark() || gAudioIO->IsStreamActive(token)) { - toolbar.SetPlay(false); //Pops toolbar.SetStop(true); //Pushes stop down // change the selection @@ -321,7 +332,6 @@ void DoPlayStopSelect(AudacityProject &project) toolbar.OnStop(evt); else if (!gAudioIO->IsBusy()) { //Otherwise, start playing (assuming audio I/O isn't busy) - //toolbar->SetPlay(true); // Not needed as set in PlayPlayRegion() toolbar.SetStop(false); // Will automatically set mLastPlayMode diff --git a/src/ProjectAudioManager.h b/src/ProjectAudioManager.h index d3a6cad4f..67bba4da7 100644 --- a/src/ProjectAudioManager.h +++ b/src/ProjectAudioManager.h @@ -36,16 +36,22 @@ public: void ResetTimerRecordCancelled() { mTimerRecordCanceled = false; } bool Paused() const { return mPaused; } - + + bool Playing() const; + // Whether recording into this project (not just into some project) is // active bool Recording() const; // Whether the last attempt to start recording requested appending to tracks bool Appending() const { return mAppending; } + bool Looping() const { return mLooping; } + bool Cutting() const { return mCutting; } void SetPaused( bool value ) { mPaused = value; } void SetAppending( bool value ) { mAppending = value; } + void SetLooping( bool value ) { mLooping = value; } + void SetCutting( bool value ) { mCutting = value; } private: // Audio IO callback methods @@ -63,6 +69,8 @@ private: bool mPaused{ false }; bool mAppending{ false }; + bool mLooping{ false }; + bool mCutting{ false }; }; AudioIOStartStreamOptions DefaultPlayOptions( AudacityProject &project ); diff --git a/src/menus/TransportMenus.cpp b/src/menus/TransportMenus.cpp index adaff0849..5cb99df4f 100644 --- a/src/menus/TransportMenus.cpp +++ b/src/menus/TransportMenus.cpp @@ -42,8 +42,7 @@ namespace { /// and pops the play button up. Then, if nothing is now /// playing, it pushes the play button down and enables /// the stop button. -bool MakeReadyToPlay(AudacityProject &project, - bool loop = false, bool cutpreview = false) +bool MakeReadyToPlay(AudacityProject &project) { auto &toolbar = ControlToolBar::Get( project ); wxCommandEvent evt; @@ -53,6 +52,7 @@ bool MakeReadyToPlay(AudacityProject &project, if (gAudioIO->IsStreamActive( ProjectAudioIO::Get( project ).GetAudioIOToken() )) { + // Make momentary changes of button appearances toolbar.SetPlay(false); //Pops toolbar.SetStop(true); //Pushes stop down toolbar.OnStop(evt); @@ -65,11 +65,6 @@ bool MakeReadyToPlay(AudacityProject &project, if (gAudioIO->IsBusy()) return false; - ControlToolBar::PlayAppearance appearance = - cutpreview ? ControlToolBar::PlayAppearance::CutPreview - : loop ? ControlToolBar::PlayAppearance::Looped - : ControlToolBar::PlayAppearance::Straight; - toolbar.SetPlay(true, appearance); toolbar.SetStop(false); return true; @@ -97,7 +92,6 @@ void DoPlayStop(const CommandContext &context) //If this project is playing, stop playing, make sure everything is unpaused. auto gAudioIO = AudioIOBase::Get(); if (gAudioIO->IsStreamActive(token)) { - toolbar.SetPlay(false); //Pops toolbar.SetStop(true); //Pushes stop down toolbar.StopPlaying(); } @@ -116,7 +110,6 @@ void DoPlayStop(const CommandContext &context) if(iter != finish) { auto otherProject = *iter; auto &otherToolbar = ControlToolBar::Get( *otherProject ); - otherToolbar.SetPlay(false); //Pops otherToolbar.SetStop(true); //Pushes stop down otherToolbar.StopPlaying(); } @@ -126,7 +119,6 @@ void DoPlayStop(const CommandContext &context) //update the playing area window.TP_DisplaySelection(); //Otherwise, start playing (assuming audio I/O isn't busy) - //toolbar->SetPlay(true); // Not needed as done in PlayPlayRegion. toolbar.SetStop(false); // Will automatically set mLastPlayMode @@ -135,7 +127,6 @@ void DoPlayStop(const CommandContext &context) } else if (!gAudioIO->IsBusy()) { //Otherwise, start playing (assuming audio I/O isn't busy) - //toolbar->SetPlay(true); // Not needed as done in PlayPlayRegion. toolbar.SetStop(false); // Will automatically set mLastPlayMode @@ -223,7 +214,7 @@ void OnPlayLooped(const CommandContext &context) { auto &project = context.project; - if( !MakeReadyToPlay(project, true) ) + if( !MakeReadyToPlay(project) ) return; // Now play in a loop @@ -790,7 +781,7 @@ void OnPlayCutPreview(const CommandContext &context) { auto &project = context.project; - if ( !MakeReadyToPlay(project, false, true) ) + if ( !MakeReadyToPlay(project) ) return; // Play with cut preview diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 42a3209d9..a2eedb66f 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -535,6 +535,8 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, bool playWhiteSpace /* = false */) // STRONG-GUARANTEE (for state of mCutPreviewTracks) { + auto &projectAudioManager = ProjectAudioManager::Get( mProject ); + if (!CanStopAudioStream()) return -1; @@ -556,23 +558,12 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, if (backwards) std::swap(t0, t1); - { - PlayAppearance appearance; - switch( mode ) { - case PlayMode::cutPreviewPlay: - appearance = PlayAppearance::CutPreview; break; - case PlayMode::loopedPlay: - appearance = PlayAppearance::Looped; break; - default: - appearance = PlayAppearance::Straight; break; - } - SetPlay(true, appearance); - } + projectAudioManager.SetLooping( mode == PlayMode::loopedPlay ); + projectAudioManager.SetCutting( mode == PlayMode::cutPreviewPlay ); bool success = false; auto cleanup = finally( [&] { if (!success) { - SetPlay(false); SetStop(false); } } ); @@ -744,25 +735,28 @@ void ControlToolBar::PlayCurrentRegion(bool looped /* = false */, void ControlToolBar::OnKeyEvent(wxKeyEvent & event) { + // PRL: is this handler really ever reached? Is the ControlToolBar ever + // focused? Isn't there a global event filter that interprets the spacebar + // key (or other key chosen in preferences) and dispatches to DoPlayStop, + // according to CommandManager's table, before we come to this redundant + // function? + if (event.ControlDown() || event.AltDown()) { event.Skip(); return; } auto gAudioIO = AudioIOBase::Get(); + auto &projectAudioManager = ProjectAudioManager::Get( mProject ); // Does not appear to be needed on Linux. Perhaps on some other platform? // If so, "!CanStopAudioStream()" should probably apply. if (event.GetKeyCode() == WXK_SPACE) { - if (gAudioIO->IsStreamActive( - ProjectAudioIO::Get( mProject ).GetAudioIOToken() - )) { - SetPlay(false); + if ( projectAudioManager.Playing() || projectAudioManager.Recording() ) { SetStop(true); StopPlaying(); } else if (!gAudioIO->IsBusy()) { - //SetPlay(true);// Not needed as done in PlayPlayRegion SetStop(false); PlayCurrentRegion(); } @@ -833,7 +827,9 @@ void ControlToolBar::StopPlaying(bool stopStream /* = true*/) SetStop(false); if(stopStream) gAudioIO->StopStream(); - SetPlay(false); + + projectAudioManager.SetLooping( false ); + projectAudioManager.SetCutting( false ); #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT gAudioIO->AILADisable(); @@ -1068,7 +1064,6 @@ bool ControlToolBar::DoRecord(AudacityProject &project, bool success = false; auto cleanup = finally([&] { if (!success) { - SetPlay(false); SetStop(false); } }); @@ -1296,6 +1291,28 @@ void ControlToolBar::OnIdle(wxIdleEvent & event) mRecord->PushDown(); mRecord->SetAlternateIdx( projectAudioManager.Appending() ? 0 : 1 ); } + + if ( !(projectAudioManager.Playing() || Scrubber::Get(mProject).HasMark()) + ) { + mPlay->PopUp(); + mPlay->SetAlternateIdx( + wxGetKeyState(WXK_CONTROL) + ? 2 + : wxGetKeyState(WXK_SHIFT) + ? 1 + : 0 + ); + } + else { + mPlay->PushDown(); + mPlay->SetAlternateIdx( + projectAudioManager.Cutting() + ? 2 + : projectAudioManager.Looping() + ? 1 + : 0 + ); + } UpdateStatusBar(); EnableDisableButtons(); diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index c018944c2..630671a30 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -330,7 +330,6 @@ void Scrubber::MarkScrubStart( mSeeking = seek; CheckMenuItems(); - ctb.SetPlay(true, ControlToolBar::PlayAppearance::Straight ); // Commented out for Bug 1421 // mSeeking // ? ControlToolBar::PlayAppearance::Seek @@ -732,14 +731,6 @@ void Scrubber::StopScrubbing() mDragging = false; mSeeking = false; - if (!IsScrubbing()) - { - // Marked scrub start, but - // didn't really play, but did change button apperance - auto &ctb = ControlToolBar::Get( *mProject ); - ctb.SetPlay(false, ControlToolBar::PlayAppearance::Straight); - } - AdornedRulerPanel::Get( *mProject ).DrawBothOverlays(); CheckMenuItems(); } From 5ab39862610c43a4d355d59c016e2fb04dadc20c Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 1 Jul 2019 08:53:53 -0400 Subject: [PATCH 6/8] Pop up the Stop button in idle time... ... This also causes a momentary push-down of the stop button, which happens in ControlToolBar::StopPlaying, really to be visible, as was apparently the intent. For instance, when playing, then clicking in the quick-play ruler to restart the play elsewhere. --- src/ProjectAudioManager.cpp | 3 +-- src/menus/TransportMenus.cpp | 10 +++------- src/toolbars/ControlToolBar.cpp | 27 ++++++--------------------- src/toolbars/ControlToolBar.h | 2 +- 4 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/ProjectAudioManager.cpp b/src/ProjectAudioManager.cpp index c80c3f661..9ac831928 100644 --- a/src/ProjectAudioManager.cpp +++ b/src/ProjectAudioManager.cpp @@ -277,7 +277,7 @@ bool DoPlayStopSelect //If busy, stop playing, make sure everything is unpaused. if (scrubber.HasMark() || gAudioIO->IsStreamActive(token)) { - toolbar.SetStop(true); //Pushes stop down + toolbar.SetStop(); //Pushes stop down // change the selection auto time = gAudioIO->GetStreamTime(); @@ -332,7 +332,6 @@ void DoPlayStopSelect(AudacityProject &project) toolbar.OnStop(evt); else if (!gAudioIO->IsBusy()) { //Otherwise, start playing (assuming audio I/O isn't busy) - toolbar.SetStop(false); // Will automatically set mLastPlayMode toolbar.PlayCurrentRegion(false); diff --git a/src/menus/TransportMenus.cpp b/src/menus/TransportMenus.cpp index 5cb99df4f..7aa92d6e3 100644 --- a/src/menus/TransportMenus.cpp +++ b/src/menus/TransportMenus.cpp @@ -54,7 +54,7 @@ bool MakeReadyToPlay(AudacityProject &project) )) { // Make momentary changes of button appearances toolbar.SetPlay(false); //Pops - toolbar.SetStop(true); //Pushes stop down + toolbar.SetStop(); //Pushes stop down toolbar.OnStop(evt); ::wxMilliSleep(100); @@ -65,8 +65,6 @@ bool MakeReadyToPlay(AudacityProject &project) if (gAudioIO->IsBusy()) return false; - toolbar.SetStop(false); - return true; } @@ -92,7 +90,7 @@ void DoPlayStop(const CommandContext &context) //If this project is playing, stop playing, make sure everything is unpaused. auto gAudioIO = AudioIOBase::Get(); if (gAudioIO->IsStreamActive(token)) { - toolbar.SetStop(true); //Pushes stop down + toolbar.SetStop(); //Pushes stop down toolbar.StopPlaying(); } else if (gAudioIO->IsStreamActive()) { @@ -110,7 +108,7 @@ void DoPlayStop(const CommandContext &context) if(iter != finish) { auto otherProject = *iter; auto &otherToolbar = ControlToolBar::Get( *otherProject ); - otherToolbar.SetStop(true); //Pushes stop down + otherToolbar.SetStop(); //Pushes stop down otherToolbar.StopPlaying(); } @@ -119,7 +117,6 @@ void DoPlayStop(const CommandContext &context) //update the playing area window.TP_DisplaySelection(); //Otherwise, start playing (assuming audio I/O isn't busy) - toolbar.SetStop(false); // Will automatically set mLastPlayMode toolbar.PlayCurrentRegion(false); @@ -127,7 +124,6 @@ void DoPlayStop(const CommandContext &context) } else if (!gAudioIO->IsBusy()) { //Otherwise, start playing (assuming audio I/O isn't busy) - toolbar.SetStop(false); // Will automatically set mLastPlayMode toolbar.PlayCurrentRegion(false); diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index a2eedb66f..e2a8f98ac 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -516,15 +516,9 @@ void ControlToolBar::SetPlay(bool down, PlayAppearance appearance) EnableDisableButtons(); } -void ControlToolBar::SetStop(bool down) +void ControlToolBar::SetStop() { - if (down) - mStop->PushDown(); - else { - if(FindFocus() == mStop) - mPlay->SetFocus(); - mStop->PopUp(); - } + mStop->PushDown(); EnableDisableButtons(); } @@ -562,11 +556,6 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, projectAudioManager.SetCutting( mode == PlayMode::cutPreviewPlay ); bool success = false; - auto cleanup = finally( [&] { - if (!success) { - SetStop(false); - } - } ); auto gAudioIO = AudioIO::Get(); if (gAudioIO->IsBusy()) @@ -753,11 +742,10 @@ void ControlToolBar::OnKeyEvent(wxKeyEvent & event) // If so, "!CanStopAudioStream()" should probably apply. if (event.GetKeyCode() == WXK_SPACE) { if ( projectAudioManager.Playing() || projectAudioManager.Recording() ) { - SetStop(true); + SetStop(); StopPlaying(); } else if (!gAudioIO->IsBusy()) { - SetStop(false); PlayCurrentRegion(); } return; @@ -824,7 +812,6 @@ void ControlToolBar::StopPlaying(bool stopStream /* = true*/) auto gAudioIO = AudioIO::Get(); - SetStop(false); if(stopStream) gAudioIO->StopStream(); @@ -1062,11 +1049,6 @@ bool ControlToolBar::DoRecord(AudacityProject &project, projectAudioManager.SetAppending( !altAppearance ); bool success = false; - auto cleanup = finally([&] { - if (!success) { - SetStop(false); - } - }); auto transportTracks = tracks; @@ -1313,6 +1295,9 @@ void ControlToolBar::OnIdle(wxIdleEvent & event) : 0 ); } + + // push-downs of the stop button are only momentary and always pop up now + mStop->PopUp(); UpdateStatusBar(); EnableDisableButtons(); diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index aae68dccd..b39faabeb 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -94,7 +94,7 @@ class ControlToolBar final : public ToolBar { //These allow buttons to be controlled externally: void SetPlay(bool down, PlayAppearance appearance = PlayAppearance::Straight); - void SetStop(bool down); + void SetStop(); // A project is only allowed to stop an audio stream that it owns. bool CanStopAudioStream () const; From 2a87ea77cf5ea47a502bf06e830dcbc91b029283 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 1 Jul 2019 09:54:52 -0400 Subject: [PATCH 7/8] Momentary Stop push-down happens in yield to the idle time handler... ... So that ControlToolBar::StopPlaying now does nothing directly to the buttons, and can be moved to another place with no dependency on ControlToolBar --- src/ProjectAudioManager.h | 4 ++++ src/toolbars/ControlToolBar.cpp | 21 +++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/ProjectAudioManager.h b/src/ProjectAudioManager.h index 67bba4da7..605916415 100644 --- a/src/ProjectAudioManager.h +++ b/src/ProjectAudioManager.h @@ -43,6 +43,8 @@ public: // active bool Recording() const; + bool Stopping() const { return mStopping; } + // Whether the last attempt to start recording requested appending to tracks bool Appending() const { return mAppending; } bool Looping() const { return mLooping; } @@ -52,6 +54,7 @@ public: void SetAppending( bool value ) { mAppending = value; } void SetLooping( bool value ) { mLooping = value; } void SetCutting( bool value ) { mCutting = value; } + void SetStopping( bool value ) { mStopping = value; } private: // Audio IO callback methods @@ -71,6 +74,7 @@ private: bool mAppending{ false }; bool mLooping{ false }; bool mCutting{ false }; + bool mStopping{ false }; }; AudioIOStartStreamOptions DefaultPlayOptions( AudacityProject &project ); diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index e2a8f98ac..1caa05c60 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -808,10 +808,20 @@ void ControlToolBar::StopPlaying(bool stopStream /* = true*/) if (!CanStopAudioStream()) return; - mStop->PushDown(); - auto gAudioIO = AudioIO::Get(); + auto cleanup = finally( [&]{ + projectAudioManager.SetStopping( false ); + } ); + + if (stopStream && gAudioIO->IsBusy()) { + // flag that we are stopping + projectAudioManager.SetStopping( true ); + // Allow UI to update for that + while( wxTheApp->ProcessIdle() ) + ; + } + if(stopStream) gAudioIO->StopStream(); @@ -1296,8 +1306,11 @@ void ControlToolBar::OnIdle(wxIdleEvent & event) ); } - // push-downs of the stop button are only momentary and always pop up now - mStop->PopUp(); + if ( projectAudioManager.Stopping() ) + mStop->PushDown(); + else + // push-downs of the stop button are only momentary and always pop up now + mStop->PopUp(); UpdateStatusBar(); EnableDisableButtons(); From 34e9cb6171351cdbc816f0a7954925237e67ed75 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 1 Jul 2019 16:32:52 -0400 Subject: [PATCH 8/8] Start and stop record or playback scrolling in idle time --- src/ProjectAudioManager.cpp | 5 ----- src/toolbars/ControlToolBar.cpp | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/ProjectAudioManager.cpp b/src/ProjectAudioManager.cpp index 9ac831928..71f6eaa6b 100644 --- a/src/ProjectAudioManager.cpp +++ b/src/ProjectAudioManager.cpp @@ -384,11 +384,6 @@ void DoTogglePinnedHead( AudacityProject &project ) TracksPrefs::SetPinnedHeadPreference(value, true); MenuManager::ModifyAllProjectToolbarMenus(); - // Change what happens in case transport is in progress right now - auto ctb = ControlToolBar::Find( *GetActiveProject() ); - if (ctb) - ctb->StartScrollingIfPreferred(); - auto &ruler = AdornedRulerPanel::Get( project ); // Update button image ruler.UpdateButtonStates(); diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 1caa05c60..deb976295 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -691,8 +691,6 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, if (!success) return -1; - StartScrollingIfPreferred(); - return token; } @@ -794,8 +792,6 @@ void ControlToolBar::PlayDefault() void ControlToolBar::StopPlaying(bool stopStream /* = true*/) { - StopScrolling(); - AudacityProject *project = &mProject; auto &projectAudioManager = ProjectAudioManager::Get( mProject ); @@ -1210,8 +1206,6 @@ bool ControlToolBar::DoRecord(AudacityProject &project, if (success) { ProjectAudioIO::Get( *p ).SetAudioIOToken(token); mBusyProject = p; - - StartScrollingIfPreferred(); } else { CancelRecording(); @@ -1275,7 +1269,8 @@ void ControlToolBar::OnIdle(wxIdleEvent & event) else mPause->PopUp(); - if (!projectAudioManager.Recording()) { + bool recording = projectAudioManager.Recording(); + if (!recording) { mRecord->PopUp(); mRecord->SetAlternateIdx( wxGetKeyState(WXK_SHIFT) ? 1 : 0 ); } @@ -1284,8 +1279,8 @@ void ControlToolBar::OnIdle(wxIdleEvent & event) mRecord->SetAlternateIdx( projectAudioManager.Appending() ? 0 : 1 ); } - if ( !(projectAudioManager.Playing() || Scrubber::Get(mProject).HasMark()) - ) { + bool playing = projectAudioManager.Playing(); + if ( !(playing || Scrubber::Get(mProject).HasMark()) ) { mPlay->PopUp(); mPlay->SetAlternateIdx( wxGetKeyState(WXK_CONTROL) @@ -1306,6 +1301,11 @@ void ControlToolBar::OnIdle(wxIdleEvent & event) ); } + if ( recording || playing ) + StartScrollingIfPreferred(); + else + StopScrolling(); + if ( projectAudioManager.Stopping() ) mStop->PushDown(); else @@ -1468,12 +1468,12 @@ void ControlToolBar::StartScrolling() using Mode = ProjectWindow::PlaybackScroller::Mode; const auto project = &mProject; if (project) { - auto gAudioIO = AudioIO::Get(); auto mode = Mode::Pinned; #if 0 // Enable these lines to pin the playhead right instead of center, // when recording but not overdubbing. + auto gAudioIO = AudioIO::Get(); if (gAudioIO->GetNumCaptureChannels() > 0) { // recording