1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-08 16:11:14 +02:00

Fix other cause of intermittent timing-dependent crash at shutdown...

... AudioIO holds a weak pointer to its listener so there is no dangling
pointer
This commit is contained in:
Paul Licameli 2019-06-24 13:32:08 -04:00
parent b8838b38ed
commit e08a942ab8
6 changed files with 48 additions and 34 deletions

View File

@ -977,7 +977,6 @@ AudioIO::AudioIO()
mNumCaptureChannels = 0; mNumCaptureChannels = 0;
mPaused = false; mPaused = false;
mListener = NULL;
mUpdateMeters = false; mUpdateMeters = false;
mUpdatingMeters = false; mUpdatingMeters = false;
@ -1445,9 +1444,10 @@ void AudioIO::StartMonitoring( const AudioIOStartStreamOptions &options )
mLastPaError = Pa_StartStream( mPortStreamV19 ); mLastPaError = Pa_StartStream( mPortStreamV19 );
// Update UI display only now, after all possibilities for error are past. // 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 // advertise the chosen I/O sample rate to the UI
mListener->OnAudioIORate((int)mRate); pListener->OnAudioIORate((int)mRate);
} }
} }
@ -1570,6 +1570,8 @@ int AudioIO::StartStream(const TransportTracks &tracks,
unsigned int captureChannels = 0; unsigned int captureChannels = 0;
sampleFormat captureFormat = floatSample; sampleFormat captureFormat = floatSample;
auto pListener = GetListener();
if (tracks.playbackTracks.size() > 0 if (tracks.playbackTracks.size() > 0
#ifdef EXPERIMENTAL_MIDI_OUT #ifdef EXPERIMENTAL_MIDI_OUT
|| tracks.midiTracks.size() > 0 || tracks.midiTracks.size() > 0
@ -1595,8 +1597,8 @@ int AudioIO::StartStream(const TransportTracks &tracks,
captureFormat = mCaptureTracks[0]->GetSampleFormat(); captureFormat = mCaptureTracks[0]->GetSampleFormat();
// Tell project that we are about to start recording // Tell project that we are about to start recording
if (mListener) if (pListener)
mListener->OnAudioIOStartRecording(); pListener->OnAudioIOStartRecording();
} }
bool successAudio; bool successAudio;
@ -1619,8 +1621,8 @@ int AudioIO::StartStream(const TransportTracks &tracks,
#endif #endif
if (!successAudio) { if (!successAudio) {
if (mListener && captureChannels > 0) if (pListener && captureChannels > 0)
mListener->OnAudioIOStopRecording(); pListener->OnAudioIOStopRecording();
mStreamToken = 0; mStreamToken = 0;
return 0; return 0;
@ -1751,8 +1753,8 @@ int AudioIO::StartStream(const TransportTracks &tracks,
{ {
mStreamToken = 0; mStreamToken = 0;
mAudioThreadFillBuffersLoopRunning = false; mAudioThreadFillBuffersLoopRunning = false;
if (mListener && mNumCaptureChannels > 0) if (pListener && mNumCaptureChannels > 0)
mListener->OnAudioIOStopRecording(); pListener->OnAudioIOStopRecording();
StartStreamCleanup(); StartStreamCleanup();
AudacityMessageBox(LAT1CTOWX(Pa_GetErrorText(err))); AudacityMessageBox(LAT1CTOWX(Pa_GetErrorText(err)));
return 0; return 0;
@ -1760,9 +1762,9 @@ int AudioIO::StartStream(const TransportTracks &tracks,
} }
// Update UI display only now, after all possibilities for error are past. // 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 // advertise the chosen I/O sample rate to the UI
mListener->OnAudioIORate((int)mRate); pListener->OnAudioIORate((int)mRate);
} }
if (mNumPlaybackChannels > 0) if (mNumPlaybackChannels > 0)
@ -2277,6 +2279,8 @@ void AudioIO::StopStream()
} }
#endif #endif
auto pListener = GetListener();
// If there's no token, we were just monitoring, so we can // If there's no token, we were just monitoring, so we can
// skip this next part... // skip this next part...
if (mStreamToken > 0) { if (mStreamToken > 0) {
@ -2354,8 +2358,8 @@ void AudioIO::StopStream()
} }
} }
if (mListener) if (pListener)
mListener->OnCommitRecording(); pListener->OnCommitRecording();
} }
} }
@ -2369,8 +2373,8 @@ void AudioIO::StopStream()
mOutputMeter.Release(); mOutputMeter.Release();
mOwningProject = nullptr; mOwningProject = nullptr;
if (mListener && mNumCaptureChannels > 0) if (pListener && mNumCaptureChannels > 0)
mListener->OnAudioIOStopRecording(); pListener->OnAudioIOStopRecording();
// //
// Only set token to 0 after we're totally finished with everything // Only set token to 0 after we're totally finished with everything
@ -2390,9 +2394,9 @@ void AudioIO::StopStream()
mScrubState.reset(); mScrubState.reset();
#endif #endif
if (mListener) { if (pListener) {
// Tell UI to hide sample rate // 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 // 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.mPosition += avail / mRate;
mRecordingSchedule.mLatencyCorrected = latencyCorrected; mRecordingSchedule.mLatencyCorrected = latencyCorrected;
if (mListener && !blockFileLog.IsEmpty()) auto pListener = GetListener();
mListener->OnAudioIONewBlockFiles(blockFileLog); if (pListener && !blockFileLog.IsEmpty())
pListener->OnAudioIONewBlockFiles(blockFileLog);
} }
// end of record buffering // 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()) if (IsBusy())
return; return;
@ -3683,8 +3689,9 @@ void AudioIoCallback::CheckSoundActivatedRecordingLevel( const void *inputBuffer
bool bShouldBePaused = mInputMeter->GetMaxPeak() < mSilenceLevel; bool bShouldBePaused = mInputMeter->GetMaxPeak() < mSilenceLevel;
if( bShouldBePaused != IsPaused()) if( bShouldBePaused != IsPaused())
{ {
if ( mListener ) auto pListener = GetListener();
mListener->OnSoundActivationThreshold(); if ( pListener )
pListener->OnSoundActivationThreshold();
} }
} }

View File

@ -19,6 +19,7 @@
#include "Experimental.h" #include "Experimental.h"
#include <memory>
#include <utility> #include <utility>
#include <wx/atomic.h> // member variable #include <wx/atomic.h> // member variable
@ -249,6 +250,10 @@ public:
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData); 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 // Part of the callback
PaStreamCallbackResult CallbackDoSeek(); PaStreamCallbackResult CallbackDoSeek();
@ -469,7 +474,7 @@ protected:
bool mUpdateMeters; bool mUpdateMeters;
volatile bool mUpdatingMeters; volatile bool mUpdatingMeters;
AudioIOListener* mListener; std::weak_ptr< AudioIOListener > mListener;
friend class AudioThread; friend class AudioThread;
#ifdef EXPERIMENTAL_MIDI_OUT #ifdef EXPERIMENTAL_MIDI_OUT
@ -558,9 +563,6 @@ public:
// This might return null during application startup or shutdown // This might return null during application startup or shutdown
static AudioIO *Get(); static AudioIO *Get();
AudioIOListener* GetListener() { return mListener; }
void SetListener(AudioIOListener* listener);
/** \brief Start up Portaudio for capture and recording as needed for /** \brief Start up Portaudio for capture and recording as needed for
* input monitoring and software playthrough only * input monitoring and software playthrough only
* *

View File

@ -74,7 +74,6 @@ struct AudioIOStartStreamOptions
AudioIOStartStreamOptions(AudacityProject *pProject_, double rate_) AudioIOStartStreamOptions(AudacityProject *pProject_, double rate_)
: pProject{ pProject_ } : pProject{ pProject_ }
, envelope(nullptr) , envelope(nullptr)
, listener(NULL)
, rate(rate_) , rate(rate_)
, playLooped(false) , playLooped(false)
, cutPreviewGapStart(0.0) , cutPreviewGapStart(0.0)
@ -86,7 +85,7 @@ struct AudioIOStartStreamOptions
AudacityProject *pProject{}; AudacityProject *pProject{};
MeterPanelBase *captureMeter{}, *playbackMeter{}; MeterPanelBase *captureMeter{}, *playbackMeter{};
BoundedEnvelope *envelope; // for time warping BoundedEnvelope *envelope; // for time warping
AudioIOListener* listener; std::shared_ptr< AudioIOListener > listener;
double rate; double rate;
bool playLooped; bool playLooped;
double cutPreviewGapStart; double cutPreviewGapStart;

View File

@ -201,7 +201,7 @@ DefaultPlayOptions( AudacityProject &project )
options.playbackMeter = projectAudioIO.GetPlaybackMeter(); options.playbackMeter = projectAudioIO.GetPlaybackMeter();
auto timeTrack = *TrackList::Get( project ).Any<TimeTrack>().begin(); auto timeTrack = *TrackList::Get( project ).Any<TimeTrack>().begin();
options.envelope = timeTrack ? timeTrack->GetEnvelope() : nullptr; options.envelope = timeTrack ? timeTrack->GetEnvelope() : nullptr;
options.listener = &ProjectAudioManager::Get( project ); options.listener = ProjectAudioManager::Get( project ).shared_from_this();
return options; return options;
} }
@ -220,6 +220,6 @@ DefaultSpeedPlayOptions( AudacityProject &project )
options.playbackMeter = projectAudioIO.GetPlaybackMeter(); options.playbackMeter = projectAudioIO.GetPlaybackMeter();
auto timeTrack = *TrackList::Get( project ).Any<TimeTrack>().begin(); auto timeTrack = *TrackList::Get( project ).Any<TimeTrack>().begin();
options.envelope = timeTrack ? timeTrack->GetEnvelope() : nullptr; options.envelope = timeTrack ? timeTrack->GetEnvelope() : nullptr;
options.listener = &ProjectAudioManager::Get( project ); options.listener = ProjectAudioManager::Get( project ).shared_from_this();
return options; return options;
} }

View File

@ -20,6 +20,7 @@ struct AudioIOStartStreamOptions;
class ProjectAudioManager final class ProjectAudioManager final
: public ClientData::Base : public ClientData::Base
, public AudioIOListener , public AudioIOListener
, public std::enable_shared_from_this< ProjectAudioManager >
{ {
public: public:
static ProjectAudioManager &Get( AudacityProject &project ); static ProjectAudioManager &Get( AudacityProject &project );

View File

@ -383,7 +383,8 @@ AudacityProject *ProjectManager::New()
//Initialise the Listeners //Initialise the Listeners
auto gAudioIO = AudioIO::Get(); auto gAudioIO = AudioIO::Get();
gAudioIO->SetListener( &ProjectAudioManager::Get( project ) ); gAudioIO->SetListener(
ProjectAudioManager::Get( project ).shared_from_this() );
auto &projectSelectionManager = ProjectSelectionManager::Get( project ); auto &projectSelectionManager = ProjectSelectionManager::Get( project );
SelectionBar::Get( project ).SetListener( &projectSelectionManager ); SelectionBar::Get( project ).SetListener( &projectSelectionManager );
#ifdef EXPERIMENTAL_SPECTRAL_EDITING #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 // Since we're going to be destroyed, make sure we're not to
// receive audio notifications anymore. // 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(); auto active = GetActiveProject();
gAudioIO->SetListener( gAudioIO->SetListener(
active ? &ProjectAudioManager::Get( *active ) : nullptr active
? ProjectAudioManager::Get( *active ).shared_from_this()
: nullptr
); );
} }