diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index de431c6d7..ba0ece6a4 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -977,7 +977,6 @@ AudioIO::AudioIO() mNumCaptureChannels = 0; mPaused = false; - mListener = NULL; mUpdateMeters = false; mUpdatingMeters = false; @@ -1445,9 +1444,10 @@ void AudioIO::StartMonitoring( const AudioIOStartStreamOptions &options ) mLastPaError = Pa_StartStream( mPortStreamV19 ); // Update UI display only now, after all possibilities for error are past. - if ((mLastPaError == paNoError) && mListener) { + auto pListener = GetListener(); + if ((mLastPaError == paNoError) && pListener) { // advertise the chosen I/O sample rate to the UI - mListener->OnAudioIORate((int)mRate); + pListener->OnAudioIORate((int)mRate); } } @@ -1570,7 +1570,9 @@ int AudioIO::StartStream(const TransportTracks &tracks, unsigned int captureChannels = 0; sampleFormat captureFormat = floatSample; - if (tracks.playbackTracks.size() > 0 + auto pListener = GetListener(); + + if (tracks.playbackTracks.size() > 0 #ifdef EXPERIMENTAL_MIDI_OUT || tracks.midiTracks.size() > 0 #endif @@ -1595,8 +1597,8 @@ int AudioIO::StartStream(const TransportTracks &tracks, captureFormat = mCaptureTracks[0]->GetSampleFormat(); // Tell project that we are about to start recording - if (mListener) - mListener->OnAudioIOStartRecording(); + if (pListener) + pListener->OnAudioIOStartRecording(); } bool successAudio; @@ -1619,8 +1621,8 @@ int AudioIO::StartStream(const TransportTracks &tracks, #endif if (!successAudio) { - if (mListener && captureChannels > 0) - mListener->OnAudioIOStopRecording(); + if (pListener && captureChannels > 0) + pListener->OnAudioIOStopRecording(); mStreamToken = 0; return 0; @@ -1751,8 +1753,8 @@ int AudioIO::StartStream(const TransportTracks &tracks, { mStreamToken = 0; mAudioThreadFillBuffersLoopRunning = false; - if (mListener && mNumCaptureChannels > 0) - mListener->OnAudioIOStopRecording(); + if (pListener && mNumCaptureChannels > 0) + pListener->OnAudioIOStopRecording(); StartStreamCleanup(); AudacityMessageBox(LAT1CTOWX(Pa_GetErrorText(err))); return 0; @@ -1760,9 +1762,9 @@ int AudioIO::StartStream(const TransportTracks &tracks, } // Update UI display only now, after all possibilities for error are past. - if (mListener) { + if (pListener) { // advertise the chosen I/O sample rate to the UI - mListener->OnAudioIORate((int)mRate); + pListener->OnAudioIORate((int)mRate); } if (mNumPlaybackChannels > 0) @@ -2277,6 +2279,8 @@ void AudioIO::StopStream() } #endif + auto pListener = GetListener(); + // If there's no token, we were just monitoring, so we can // skip this next part... if (mStreamToken > 0) { @@ -2354,8 +2358,8 @@ void AudioIO::StopStream() } } - if (mListener) - mListener->OnCommitRecording(); + if (pListener) + pListener->OnCommitRecording(); } } @@ -2369,8 +2373,8 @@ void AudioIO::StopStream() mOutputMeter.Release(); mOwningProject = nullptr; - if (mListener && mNumCaptureChannels > 0) - mListener->OnAudioIOStopRecording(); + if (pListener && mNumCaptureChannels > 0) + pListener->OnAudioIOStopRecording(); // // Only set token to 0 after we're totally finished with everything @@ -2390,9 +2394,9 @@ void AudioIO::StopStream() mScrubState.reset(); #endif - if (mListener) { + if (pListener) { // Tell UI to hide sample rate - mListener->OnAudioIORate(0); + pListener->OnAudioIORate(0); } // Don't cause a busy wait in the audio thread after stopping scrubbing @@ -2983,8 +2987,9 @@ void AudioIO::FillBuffers() mRecordingSchedule.mPosition += avail / mRate; mRecordingSchedule.mLatencyCorrected = latencyCorrected; - if (mListener && !blockFileLog.IsEmpty()) - mListener->OnAudioIONewBlockFiles(blockFileLog); + auto pListener = GetListener(); + if (pListener && !blockFileLog.IsEmpty()) + pListener->OnAudioIONewBlockFiles(blockFileLog); } // end of record buffering }, @@ -3004,7 +3009,8 @@ void AudioIO::FillBuffers() ); } -void AudioIO::SetListener(AudioIOListener* listener) +void AudioIoCallback::SetListener( + const std::shared_ptr< AudioIOListener > &listener) { if (IsBusy()) return; @@ -3683,8 +3689,9 @@ void AudioIoCallback::CheckSoundActivatedRecordingLevel( const void *inputBuffer bool bShouldBePaused = mInputMeter->GetMaxPeak() < mSilenceLevel; if( bShouldBePaused != IsPaused()) { - if ( mListener ) - mListener->OnSoundActivationThreshold(); + auto pListener = GetListener(); + if ( pListener ) + pListener->OnSoundActivationThreshold(); } } diff --git a/src/AudioIO.h b/src/AudioIO.h index 215d76149..c75ee147c 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -19,6 +19,7 @@ #include "Experimental.h" +#include #include #include // member variable @@ -249,6 +250,10 @@ public: const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags, void *userData); + std::shared_ptr< AudioIOListener > GetListener() const + { return mListener.lock(); } + void SetListener( const std::shared_ptr< AudioIOListener > &listener); + // Part of the callback PaStreamCallbackResult CallbackDoSeek(); @@ -469,7 +474,7 @@ protected: bool mUpdateMeters; volatile bool mUpdatingMeters; - AudioIOListener* mListener; + std::weak_ptr< AudioIOListener > mListener; friend class AudioThread; #ifdef EXPERIMENTAL_MIDI_OUT @@ -558,9 +563,6 @@ public: // This might return null during application startup or shutdown static AudioIO *Get(); - AudioIOListener* GetListener() { return mListener; } - void SetListener(AudioIOListener* listener); - /** \brief Start up Portaudio for capture and recording as needed for * input monitoring and software playthrough only * diff --git a/src/AudioIOBase.h b/src/AudioIOBase.h index d72391546..1586c9206 100644 --- a/src/AudioIOBase.h +++ b/src/AudioIOBase.h @@ -74,7 +74,6 @@ struct AudioIOStartStreamOptions AudioIOStartStreamOptions(AudacityProject *pProject_, double rate_) : pProject{ pProject_ } , envelope(nullptr) - , listener(NULL) , rate(rate_) , playLooped(false) , cutPreviewGapStart(0.0) @@ -86,7 +85,7 @@ struct AudioIOStartStreamOptions AudacityProject *pProject{}; MeterPanelBase *captureMeter{}, *playbackMeter{}; BoundedEnvelope *envelope; // for time warping - AudioIOListener* listener; + std::shared_ptr< AudioIOListener > listener; double rate; bool playLooped; double cutPreviewGapStart; diff --git a/src/ProjectAudioManager.cpp b/src/ProjectAudioManager.cpp index e7e7cec35..9ae53423d 100644 --- a/src/ProjectAudioManager.cpp +++ b/src/ProjectAudioManager.cpp @@ -201,7 +201,7 @@ DefaultPlayOptions( AudacityProject &project ) options.playbackMeter = projectAudioIO.GetPlaybackMeter(); auto timeTrack = *TrackList::Get( project ).Any().begin(); options.envelope = timeTrack ? timeTrack->GetEnvelope() : nullptr; - options.listener = &ProjectAudioManager::Get( project ); + options.listener = ProjectAudioManager::Get( project ).shared_from_this(); return options; } @@ -220,6 +220,6 @@ DefaultSpeedPlayOptions( AudacityProject &project ) options.playbackMeter = projectAudioIO.GetPlaybackMeter(); auto timeTrack = *TrackList::Get( project ).Any().begin(); options.envelope = timeTrack ? timeTrack->GetEnvelope() : nullptr; - options.listener = &ProjectAudioManager::Get( project ); + options.listener = ProjectAudioManager::Get( project ).shared_from_this(); return options; } diff --git a/src/ProjectAudioManager.h b/src/ProjectAudioManager.h index 622f598ce..81eec1e8a 100644 --- a/src/ProjectAudioManager.h +++ b/src/ProjectAudioManager.h @@ -20,6 +20,7 @@ struct AudioIOStartStreamOptions; class ProjectAudioManager final : public ClientData::Base , public AudioIOListener + , public std::enable_shared_from_this< ProjectAudioManager > { public: static ProjectAudioManager &Get( AudacityProject &project ); diff --git a/src/ProjectManager.cpp b/src/ProjectManager.cpp index a85b85b18..f88d20359 100644 --- a/src/ProjectManager.cpp +++ b/src/ProjectManager.cpp @@ -383,7 +383,8 @@ AudacityProject *ProjectManager::New() //Initialise the Listeners auto gAudioIO = AudioIO::Get(); - gAudioIO->SetListener( &ProjectAudioManager::Get( project ) ); + gAudioIO->SetListener( + ProjectAudioManager::Get( project ).shared_from_this() ); auto &projectSelectionManager = ProjectSelectionManager::Get( project ); SelectionBar::Get( project ).SetListener( &projectSelectionManager ); #ifdef EXPERIMENTAL_SPECTRAL_EDITING @@ -599,10 +600,14 @@ void ProjectManager::OnCloseWindow(wxCloseEvent & event) // Since we're going to be destroyed, make sure we're not to // receive audio notifications anymore. - if ( gAudioIO->GetListener() == &ProjectAudioManager::Get( project ) ) { + // PRL: Maybe all this is unnecessary now that the listener is managed + // by a weak pointer. + if ( gAudioIO->GetListener().get() == &ProjectAudioManager::Get( project ) ) { auto active = GetActiveProject(); gAudioIO->SetListener( - active ? &ProjectAudioManager::Get( *active ) : nullptr + active + ? ProjectAudioManager::Get( *active ).shared_from_this() + : nullptr ); }