mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-22 15:20:15 +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 */
|
||||
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()) {
|
||||
SetPlay(false);
|
||||
bool success = false;
|
||||
auto cleanup = finally( [&] {
|
||||
if (!success) {
|
||||
SetPlay(false);
|
||||
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,13 +647,9 @@ 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());
|
||||
|
||||
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) {
|
||||
TrackList *trackList = p->GetTracks();
|
||||
TrackListIterator it(trackList);
|
||||
|
||||
bool shifted = mRecord->WasShiftDown();
|
||||
#ifdef EXPERIMENTAL_DA
|
||||
shifted = !shifted;
|
||||
#endif
|
||||
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)
|
||||
|
@ -2672,19 +2672,21 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
|
||||
ClearPlayRegion();
|
||||
}
|
||||
|
||||
StartQPPlay(evt.ShiftDown(), evt.ControlDown());
|
||||
|
||||
mMouseEventState = mesNone;
|
||||
mIsDragging = false;
|
||||
mLeftDownClick = -1;
|
||||
|
||||
if (mPlayRegionLock) {
|
||||
// Restore Locked Play region
|
||||
SetPlayRegion(mOldPlayRegionStart, mOldPlayRegionEnd);
|
||||
mProject->OnLockPlayRegion();
|
||||
// and release local lock
|
||||
mPlayRegionLock = false;
|
||||
}
|
||||
auto cleanup = finally( [&] {
|
||||
if (mPlayRegionLock) {
|
||||
// Restore Locked Play region
|
||||
SetPlayRegion(mOldPlayRegionStart, mOldPlayRegionEnd);
|
||||
mProject->OnLockPlayRegion();
|
||||
// and release local lock
|
||||
mPlayRegionLock = false;
|
||||
}
|
||||
} );
|
||||
|
||||
StartQPPlay(evt.ShiftDown(), evt.ControlDown());
|
||||
}
|
||||
|
||||
void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview)
|
||||
@ -2732,18 +2734,20 @@ void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview)
|
||||
options.timeTrack = NULL;
|
||||
|
||||
ControlToolBar::PlayAppearance appearance =
|
||||
cutPreview ? ControlToolBar::PlayAppearance::CutPreview
|
||||
: options.playLooped ? ControlToolBar::PlayAppearance::Looped
|
||||
: ControlToolBar::PlayAppearance::Straight;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user