mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-10 09:01:13 +02:00
Exception safety in: ControlToolBar & Ruler functions...
... because SetupCutPreviewTracks has a small chance of throwing for want of disk space. StopStream however is considered nonthrowing.
This commit is contained in:
parent
0bb6a3d971
commit
2cbdd1cc43
@ -508,6 +508,7 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
PlayAppearance appearance, /* = PlayOption::Straight */
|
PlayAppearance appearance, /* = PlayOption::Straight */
|
||||||
bool backwards, /* = false */
|
bool backwards, /* = false */
|
||||||
bool playWhiteSpace /* = false */)
|
bool playWhiteSpace /* = false */)
|
||||||
|
// STRONG-GUARANTEE (for state of mCutPreviewTracks)
|
||||||
{
|
{
|
||||||
if (!CanStopAudioStream())
|
if (!CanStopAudioStream())
|
||||||
return -1;
|
return -1;
|
||||||
@ -526,28 +527,29 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
|
|
||||||
SetPlay(true, appearance);
|
SetPlay(true, appearance);
|
||||||
|
|
||||||
if (gAudioIO->IsBusy()) {
|
bool success = false;
|
||||||
SetPlay(false);
|
auto cleanup = finally( [&] {
|
||||||
|
if (!success) {
|
||||||
|
SetPlay(false);
|
||||||
|
SetStop(false);
|
||||||
|
SetRecord(false);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
if (gAudioIO->IsBusy())
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
const bool cutpreview = appearance == PlayAppearance::CutPreview;
|
const bool cutpreview = appearance == PlayAppearance::CutPreview;
|
||||||
if (cutpreview && t0==t1) {
|
if (cutpreview && t0==t1)
|
||||||
SetPlay(false);
|
|
||||||
return -1; /* msmeyer: makes no sense */
|
return -1; /* msmeyer: makes no sense */
|
||||||
}
|
|
||||||
|
|
||||||
AudacityProject *p = GetActiveProject();
|
AudacityProject *p = GetActiveProject();
|
||||||
if (!p) {
|
if (!p)
|
||||||
SetPlay(false);
|
|
||||||
return -1; // Should never happen, but...
|
return -1; // Should never happen, but...
|
||||||
}
|
|
||||||
|
|
||||||
TrackList *t = p->GetTracks();
|
TrackList *t = p->GetTracks();
|
||||||
if (!t) {
|
if (!t)
|
||||||
mPlay->PopUp();
|
|
||||||
return -1; // Should never happen, but...
|
return -1; // Should never happen, but...
|
||||||
}
|
|
||||||
|
|
||||||
p->mLastPlayMode = mode;
|
p->mLastPlayMode = mode;
|
||||||
|
|
||||||
@ -566,10 +568,8 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
|
|
||||||
double latestEnd = (playWhiteSpace)? t1 : t->GetEndTime();
|
double latestEnd = (playWhiteSpace)? t1 : t->GetEndTime();
|
||||||
|
|
||||||
if (!hasaudio) {
|
if (!hasaudio)
|
||||||
SetPlay(false);
|
|
||||||
return -1; // No need to continue without audio tracks
|
return -1; // No need to continue without audio tracks
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
|
#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
|
||||||
double init_seek = 0.0;
|
double init_seek = 0.0;
|
||||||
@ -620,7 +620,7 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int token = -1;
|
int token = -1;
|
||||||
bool success = false;
|
|
||||||
if (t1 != t0) {
|
if (t1 != t0) {
|
||||||
if (cutpreview) {
|
if (cutpreview) {
|
||||||
const double tless = std::min(t0, t1);
|
const double tless = std::min(t0, t1);
|
||||||
@ -647,13 +647,9 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
#endif
|
#endif
|
||||||
tcp0, tcp1, myOptions);
|
tcp0, tcp1, myOptions);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
// Cannot create cut preview tracks, clean up and exit
|
// Cannot create cut preview tracks, clean up and exit
|
||||||
SetPlay(false);
|
|
||||||
SetStop(false);
|
|
||||||
SetRecord(false);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Lifted the following into AudacityProject::GetDefaultPlayOptions()
|
// Lifted the following into AudacityProject::GetDefaultPlayOptions()
|
||||||
@ -687,12 +683,8 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success)
|
||||||
SetPlay(false);
|
|
||||||
SetStop(false);
|
|
||||||
SetRecord(false);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
StartScrollingIfPreferred();
|
StartScrollingIfPreferred();
|
||||||
|
|
||||||
@ -768,8 +760,8 @@ void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt))
|
|||||||
|
|
||||||
if (p) p->TP_DisplaySelection();
|
if (p) p->TP_DisplaySelection();
|
||||||
|
|
||||||
|
auto cleanup = finally( [&]{ UpdateStatusBar(p); } );
|
||||||
PlayDefault();
|
PlayDefault();
|
||||||
UpdateStatusBar(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt))
|
void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt))
|
||||||
@ -861,6 +853,7 @@ void ControlToolBar::Pause()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
||||||
|
// STRONG-GUARANTEE (for state of current project's tracks)
|
||||||
{
|
{
|
||||||
if (gAudioIO->IsBusy()) {
|
if (gAudioIO->IsBusy()) {
|
||||||
if (!CanStopAudioStream() || 0 == gAudioIO->GetNumCaptureChannels())
|
if (!CanStopAudioStream() || 0 == gAudioIO->GetNumCaptureChannels())
|
||||||
@ -878,14 +871,43 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
|||||||
|
|
||||||
SetRecord(true, mRecord->WasShiftDown());
|
SetRecord(true, mRecord->WasShiftDown());
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
bool shifted = mRecord->WasShiftDown();
|
||||||
|
#ifdef EXPERIMENTAL_DA
|
||||||
|
shifted = !shifted;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TrackList *trackList = p->GetTracks();
|
||||||
|
TrackList tracksCopy{};
|
||||||
|
bool tracksCopied = false;
|
||||||
|
|
||||||
|
WaveTrackArray recordingTracks;
|
||||||
|
|
||||||
|
auto cleanup = finally( [&] {
|
||||||
|
if (!success) {
|
||||||
|
if (tracksCopied)
|
||||||
|
// Restore the tracks to remove any inserted silence
|
||||||
|
*trackList = std::move(tracksCopy);
|
||||||
|
|
||||||
|
if ( ! shifted ) {
|
||||||
|
// msmeyer: Delete recently added tracks if opening stream fails
|
||||||
|
for ( auto track : recordingTracks )
|
||||||
|
trackList->Remove(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPlay(false);
|
||||||
|
SetStop(false);
|
||||||
|
SetRecord(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success or not:
|
||||||
|
UpdateStatusBar(GetActiveProject());
|
||||||
|
} );
|
||||||
|
|
||||||
if (p) {
|
if (p) {
|
||||||
TrackList *trackList = p->GetTracks();
|
|
||||||
TrackListIterator it(trackList);
|
TrackListIterator it(trackList);
|
||||||
|
|
||||||
bool shifted = mRecord->WasShiftDown();
|
|
||||||
#ifdef EXPERIMENTAL_DA
|
|
||||||
shifted = !shifted;
|
|
||||||
#endif
|
|
||||||
bool hasWave = false;
|
bool hasWave = false;
|
||||||
for (auto t = it.First(); t; t = it.Next()) {
|
for (auto t = it.First(); t; t = it.Next()) {
|
||||||
if (t->GetKind() == Track::Wave) {
|
if (t->GetKind() == Track::Wave) {
|
||||||
@ -905,7 +927,6 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
|||||||
|
|
||||||
/* TODO: set up stereo tracks if that is how the user has set up
|
/* TODO: set up stereo tracks if that is how the user has set up
|
||||||
* their preferences, and choose sample format based on prefs */
|
* their preferences, and choose sample format based on prefs */
|
||||||
WaveTrackArray newRecordingTracks;
|
|
||||||
WaveTrackConstArray playbackTracks;
|
WaveTrackConstArray playbackTracks;
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
NoteTrackArray midiTracks;
|
NoteTrackArray midiTracks;
|
||||||
@ -928,8 +949,6 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
|||||||
|
|
||||||
// If SHIFT key was down, the user wants append to tracks
|
// If SHIFT key was down, the user wants append to tracks
|
||||||
int recordingChannels = 0;
|
int recordingChannels = 0;
|
||||||
TrackList tracksCopy{};
|
|
||||||
bool tracksCopied = false;
|
|
||||||
|
|
||||||
if (shifted) {
|
if (shifted) {
|
||||||
recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2);
|
recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2);
|
||||||
@ -975,10 +994,15 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
|||||||
t1 = wt->GetEndTime();
|
t1 = wt->GetEndTime();
|
||||||
if (t1 < t0) {
|
if (t1 < t0) {
|
||||||
if (!tracksCopied) {
|
if (!tracksCopied) {
|
||||||
|
// Duplicate all tracks before modifying any of them.
|
||||||
|
// The duplicates are used to restore state in case
|
||||||
|
// of failure.
|
||||||
tracksCopied = true;
|
tracksCopied = true;
|
||||||
tracksCopy = *trackList;
|
tracksCopy = *trackList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pad the recording track with silence, up to the
|
||||||
|
// maximum time.
|
||||||
auto newTrack = p->GetTrackFactory()->NewWaveTrack();
|
auto newTrack = p->GetTrackFactory()->NewWaveTrack();
|
||||||
newTrack->InsertSilence(0.0, t0 - t1);
|
newTrack->InsertSilence(0.0, t0 - t1);
|
||||||
newTrack->Flush();
|
newTrack->Flush();
|
||||||
@ -987,9 +1011,9 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
|||||||
wxASSERT(bResult); // TO DO: Actually handle this.
|
wxASSERT(bResult); // TO DO: Actually handle this.
|
||||||
wxUnusedVar(bResult);
|
wxUnusedVar(bResult);
|
||||||
}
|
}
|
||||||
newRecordingTracks.push_back(wt);
|
recordingTracks.push_back(wt);
|
||||||
// Don't record more channels than configured recording pref.
|
// Don't record more channels than configured recording pref.
|
||||||
if( (int)newRecordingTracks.size() >= recordingChannels ){
|
if( (int)recordingTracks.size() >= recordingChannels ){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1076,7 +1100,7 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let the list hold the track, and keep a pointer to it
|
// Let the list hold the track, and keep a pointer to it
|
||||||
newRecordingTracks.push_back(
|
recordingTracks.push_back(
|
||||||
static_cast<WaveTrack*>(
|
static_cast<WaveTrack*>(
|
||||||
trackList->Add(
|
trackList->Add(
|
||||||
std::move(newTrack))));
|
std::move(newTrack))));
|
||||||
@ -1090,13 +1114,13 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
|||||||
|
|
||||||
AudioIOStartStreamOptions options(p->GetDefaultPlayOptions());
|
AudioIOStartStreamOptions options(p->GetDefaultPlayOptions());
|
||||||
int token = gAudioIO->StartStream(playbackTracks,
|
int token = gAudioIO->StartStream(playbackTracks,
|
||||||
newRecordingTracks,
|
recordingTracks,
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
midiTracks,
|
midiTracks,
|
||||||
#endif
|
#endif
|
||||||
t0, t1, options);
|
t0, t1, options);
|
||||||
|
|
||||||
bool success = (token != 0);
|
success = (token != 0);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
p->SetAudioIOToken(token);
|
p->SetAudioIOToken(token);
|
||||||
@ -1105,28 +1129,11 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
|
|||||||
StartScrollingIfPreferred();
|
StartScrollingIfPreferred();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (shifted) {
|
|
||||||
// Restore the tracks to remove any inserted silence
|
|
||||||
if (tracksCopied)
|
|
||||||
*trackList = std::move(tracksCopy);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// msmeyer: Delete recently added tracks if opening stream fails
|
|
||||||
for (unsigned int i = 0; i < newRecordingTracks.size(); i++) {
|
|
||||||
trackList->Remove(newRecordingTracks[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// msmeyer: Show error message if stream could not be opened
|
// msmeyer: Show error message if stream could not be opened
|
||||||
wxMessageBox(_("Error opening sound device. Try changing the audio host, recording device and the project sample rate."),
|
wxMessageBox(_("Error opening sound device. Try changing the audio host, recording device and the project sample rate."),
|
||||||
_("Error"), wxOK | wxICON_EXCLAMATION, this);
|
_("Error"), wxOK | wxICON_EXCLAMATION, this);
|
||||||
|
|
||||||
SetPlay(false);
|
|
||||||
SetStop(false);
|
|
||||||
SetRecord(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UpdateStatusBar(GetActiveProject());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1187,6 +1194,8 @@ void ControlToolBar::OnFF(wxCommandEvent & WXUNUSED(evt))
|
|||||||
|
|
||||||
void ControlToolBar::SetupCutPreviewTracks(double WXUNUSED(playStart), double cutStart,
|
void ControlToolBar::SetupCutPreviewTracks(double WXUNUSED(playStart), double cutStart,
|
||||||
double cutEnd, double WXUNUSED(playEnd))
|
double cutEnd, double WXUNUSED(playEnd))
|
||||||
|
|
||||||
|
// STRONG-GUARANTEE (for state of mCutPreviewTracks)
|
||||||
{
|
{
|
||||||
ClearCutPreviewTracks();
|
ClearCutPreviewTracks();
|
||||||
AudacityProject *p = GetActiveProject();
|
AudacityProject *p = GetActiveProject();
|
||||||
@ -1207,6 +1216,7 @@ void ControlToolBar::SetupCutPreviewTracks(double WXUNUSED(playStart), double cu
|
|||||||
if (track1)
|
if (track1)
|
||||||
{
|
{
|
||||||
// Duplicate and change tracks
|
// Duplicate and change tracks
|
||||||
|
// Clear has a very small chance of throwing
|
||||||
auto new1 = track1->Duplicate();
|
auto new1 = track1->Duplicate();
|
||||||
new1->Clear(cutStart, cutEnd);
|
new1->Clear(cutStart, cutEnd);
|
||||||
decltype(new1) new2{};
|
decltype(new1) new2{};
|
||||||
@ -1216,6 +1226,8 @@ void ControlToolBar::SetupCutPreviewTracks(double WXUNUSED(playStart), double cu
|
|||||||
new2->Clear(cutStart, cutEnd);
|
new2->Clear(cutStart, cutEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use NOTHROW-GUARANTEE:
|
||||||
|
|
||||||
mCutPreviewTracks = std::make_unique<TrackList>();
|
mCutPreviewTracks = std::make_unique<TrackList>();
|
||||||
mCutPreviewTracks->Add(std::move(new1));
|
mCutPreviewTracks->Add(std::move(new1));
|
||||||
if (track2)
|
if (track2)
|
||||||
|
@ -2672,19 +2672,21 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
|
|||||||
ClearPlayRegion();
|
ClearPlayRegion();
|
||||||
}
|
}
|
||||||
|
|
||||||
StartQPPlay(evt.ShiftDown(), evt.ControlDown());
|
|
||||||
|
|
||||||
mMouseEventState = mesNone;
|
mMouseEventState = mesNone;
|
||||||
mIsDragging = false;
|
mIsDragging = false;
|
||||||
mLeftDownClick = -1;
|
mLeftDownClick = -1;
|
||||||
|
|
||||||
if (mPlayRegionLock) {
|
auto cleanup = finally( [&] {
|
||||||
// Restore Locked Play region
|
if (mPlayRegionLock) {
|
||||||
SetPlayRegion(mOldPlayRegionStart, mOldPlayRegionEnd);
|
// Restore Locked Play region
|
||||||
mProject->OnLockPlayRegion();
|
SetPlayRegion(mOldPlayRegionStart, mOldPlayRegionEnd);
|
||||||
// and release local lock
|
mProject->OnLockPlayRegion();
|
||||||
mPlayRegionLock = false;
|
// and release local lock
|
||||||
}
|
mPlayRegionLock = false;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
StartQPPlay(evt.ShiftDown(), evt.ControlDown());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview)
|
void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview)
|
||||||
@ -2732,18 +2734,20 @@ void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview)
|
|||||||
options.timeTrack = NULL;
|
options.timeTrack = NULL;
|
||||||
|
|
||||||
ControlToolBar::PlayAppearance appearance =
|
ControlToolBar::PlayAppearance appearance =
|
||||||
cutPreview ? ControlToolBar::PlayAppearance::CutPreview
|
cutPreview ? ControlToolBar::PlayAppearance::CutPreview
|
||||||
: options.playLooped ? ControlToolBar::PlayAppearance::Looped
|
: options.playLooped ? ControlToolBar::PlayAppearance::Looped
|
||||||
: ControlToolBar::PlayAppearance::Straight;
|
: ControlToolBar::PlayAppearance::Straight;
|
||||||
|
|
||||||
|
mPlayRegionStart = start;
|
||||||
|
mPlayRegionEnd = end;
|
||||||
|
Refresh();
|
||||||
|
|
||||||
ctb->PlayPlayRegion((SelectedRegion(start, end)),
|
ctb->PlayPlayRegion((SelectedRegion(start, end)),
|
||||||
options, PlayMode::normalPlay,
|
options, PlayMode::normalPlay,
|
||||||
appearance,
|
appearance,
|
||||||
false,
|
false,
|
||||||
true);
|
true);
|
||||||
|
|
||||||
mPlayRegionStart = start;
|
|
||||||
mPlayRegionEnd = end;
|
|
||||||
Refresh();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user