1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-22 23:30:07 +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:
Paul Licameli 2016-12-03 09:29:58 -05:00
parent 0bb6a3d971
commit 2cbdd1cc43
2 changed files with 88 additions and 72 deletions

View File

@ -508,6 +508,7 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
PlayAppearance appearance, /* = PlayOption::Straight */
bool backwards, /* = false */
bool playWhiteSpace /* = false */)
// STRONG-GUARANTEE (for state of mCutPreviewTracks)
{
if (!CanStopAudioStream())
return -1;
@ -526,28 +527,29 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
SetPlay(true, appearance);
if (gAudioIO->IsBusy()) {
bool success = false;
auto cleanup = finally( [&] {
if (!success) {
SetPlay(false);
return -1;
SetStop(false);
SetRecord(false);
}
} );
if (gAudioIO->IsBusy())
return -1;
const bool cutpreview = appearance == PlayAppearance::CutPreview;
if (cutpreview && t0==t1) {
SetPlay(false);
if (cutpreview && t0==t1)
return -1; /* msmeyer: makes no sense */
}
AudacityProject *p = GetActiveProject();
if (!p) {
SetPlay(false);
if (!p)
return -1; // Should never happen, but...
}
TrackList *t = p->GetTracks();
if (!t) {
mPlay->PopUp();
if (!t)
return -1; // Should never happen, but...
}
p->mLastPlayMode = mode;
@ -566,10 +568,8 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
double latestEnd = (playWhiteSpace)? t1 : t->GetEndTime();
if (!hasaudio) {
SetPlay(false);
if (!hasaudio)
return -1; // No need to continue without audio tracks
}
#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
double init_seek = 0.0;
@ -620,7 +620,7 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
}
int token = -1;
bool success = false;
if (t1 != t0) {
if (cutpreview) {
const double tless = std::min(t0, t1);
@ -647,14 +647,10 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
#endif
tcp0, tcp1, myOptions);
}
else {
else
// Cannot create cut preview tracks, clean up and exit
SetPlay(false);
SetStop(false);
SetRecord(false);
return -1;
}
}
else {
// Lifted the following into AudacityProject::GetDefaultPlayOptions()
/*
@ -687,12 +683,8 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
}
}
if (!success) {
SetPlay(false);
SetStop(false);
SetRecord(false);
if (!success)
return -1;
}
StartScrollingIfPreferred();
@ -768,8 +760,8 @@ void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt))
if (p) p->TP_DisplaySelection();
auto cleanup = finally( [&]{ UpdateStatusBar(p); } );
PlayDefault();
UpdateStatusBar(p);
}
void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt))
@ -861,6 +853,7 @@ void ControlToolBar::Pause()
}
void ControlToolBar::OnRecord(wxCommandEvent &evt)
// STRONG-GUARANTEE (for state of current project's tracks)
{
if (gAudioIO->IsBusy()) {
if (!CanStopAudioStream() || 0 == gAudioIO->GetNumCaptureChannels())
@ -878,14 +871,43 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
SetRecord(true, mRecord->WasShiftDown());
if (p) {
TrackList *trackList = p->GetTracks();
TrackListIterator it(trackList);
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) {
TrackListIterator it(trackList);
bool hasWave = false;
for (auto t = it.First(); t; t = it.Next()) {
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
* their preferences, and choose sample format based on prefs */
WaveTrackArray newRecordingTracks;
WaveTrackConstArray playbackTracks;
#ifdef EXPERIMENTAL_MIDI_OUT
NoteTrackArray midiTracks;
@ -928,8 +949,6 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
// If SHIFT key was down, the user wants append to tracks
int recordingChannels = 0;
TrackList tracksCopy{};
bool tracksCopied = false;
if (shifted) {
recordingChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2);
@ -975,10 +994,15 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
t1 = wt->GetEndTime();
if (t1 < t0) {
if (!tracksCopied) {
// Duplicate all tracks before modifying any of them.
// The duplicates are used to restore state in case
// of failure.
tracksCopied = true;
tracksCopy = *trackList;
}
// Pad the recording track with silence, up to the
// maximum time.
auto newTrack = p->GetTrackFactory()->NewWaveTrack();
newTrack->InsertSilence(0.0, t0 - t1);
newTrack->Flush();
@ -987,9 +1011,9 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
wxASSERT(bResult); // TO DO: Actually handle this.
wxUnusedVar(bResult);
}
newRecordingTracks.push_back(wt);
recordingTracks.push_back(wt);
// Don't record more channels than configured recording pref.
if( (int)newRecordingTracks.size() >= recordingChannels ){
if( (int)recordingTracks.size() >= recordingChannels ){
break;
}
}
@ -1076,7 +1100,7 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
}
// Let the list hold the track, and keep a pointer to it
newRecordingTracks.push_back(
recordingTracks.push_back(
static_cast<WaveTrack*>(
trackList->Add(
std::move(newTrack))));
@ -1090,13 +1114,13 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
AudioIOStartStreamOptions options(p->GetDefaultPlayOptions());
int token = gAudioIO->StartStream(playbackTracks,
newRecordingTracks,
recordingTracks,
#ifdef EXPERIMENTAL_MIDI_OUT
midiTracks,
#endif
t0, t1, options);
bool success = (token != 0);
success = (token != 0);
if (success) {
p->SetAudioIOToken(token);
@ -1105,28 +1129,11 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt)
StartScrollingIfPreferred();
}
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
wxMessageBox(_("Error opening sound device. Try changing the audio host, recording device and the project sample rate."),
_("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,
double cutEnd, double WXUNUSED(playEnd))
// STRONG-GUARANTEE (for state of mCutPreviewTracks)
{
ClearCutPreviewTracks();
AudacityProject *p = GetActiveProject();
@ -1207,6 +1216,7 @@ void ControlToolBar::SetupCutPreviewTracks(double WXUNUSED(playStart), double cu
if (track1)
{
// Duplicate and change tracks
// Clear has a very small chance of throwing
auto new1 = track1->Duplicate();
new1->Clear(cutStart, cutEnd);
decltype(new1) new2{};
@ -1216,6 +1226,8 @@ void ControlToolBar::SetupCutPreviewTracks(double WXUNUSED(playStart), double cu
new2->Clear(cutStart, cutEnd);
}
// use NOTHROW-GUARANTEE:
mCutPreviewTracks = std::make_unique<TrackList>();
mCutPreviewTracks->Add(std::move(new1));
if (track2)

View File

@ -2672,12 +2672,11 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
ClearPlayRegion();
}
StartQPPlay(evt.ShiftDown(), evt.ControlDown());
mMouseEventState = mesNone;
mIsDragging = false;
mLeftDownClick = -1;
auto cleanup = finally( [&] {
if (mPlayRegionLock) {
// Restore Locked Play region
SetPlayRegion(mOldPlayRegionStart, mOldPlayRegionEnd);
@ -2685,6 +2684,9 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
// and release local lock
mPlayRegionLock = false;
}
} );
StartQPPlay(evt.ShiftDown(), evt.ControlDown());
}
void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview)
@ -2735,15 +2737,17 @@ void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview)
cutPreview ? ControlToolBar::PlayAppearance::CutPreview
: options.playLooped ? ControlToolBar::PlayAppearance::Looped
: ControlToolBar::PlayAppearance::Straight;
mPlayRegionStart = start;
mPlayRegionEnd = end;
Refresh();
ctb->PlayPlayRegion((SelectedRegion(start, end)),
options, PlayMode::normalPlay,
appearance,
false,
true);
mPlayRegionStart = start;
mPlayRegionEnd = end;
Refresh();
}
}