mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-09 00:21:16 +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:
parent
b8838b38ed
commit
e08a942ab8
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
*
|
*
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 );
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user