diff --git a/src/AudacityException.cpp b/src/AudacityException.cpp index 46504d4cc..e37edf175 100644 --- a/src/AudacityException.cpp +++ b/src/AudacityException.cpp @@ -50,6 +50,21 @@ MessageBoxException::~MessageBoxException() wxAtomicDec( sOutstandingMessages ); } +SimpleMessageBoxException::~SimpleMessageBoxException() +{ +} + +wxString SimpleMessageBoxException::ErrorMessage() const +{ + return message; +} + +std::unique_ptr< AudacityException > SimpleMessageBoxException::Move() +{ + return std::unique_ptr< AudacityException > + { safenew SimpleMessageBoxException{ std::move( *this ) } }; +} + // This is meant to be invoked via wxEvtHandler::CallAfter void MessageBoxException::DelayedHandlerAction() { diff --git a/src/AudacityException.h b/src/AudacityException.h index a0960fbdc..31e2e3382 100644 --- a/src/AudacityException.h +++ b/src/AudacityException.h @@ -69,6 +69,30 @@ private: mutable bool moved { false }; }; +// MessageBoxException that shows a given, unvarying string. +class SimpleMessageBoxException /* not final */ : public MessageBoxException +{ +public: + explicit SimpleMessageBoxException( const wxString &message_, + const wxString &caption = wxString{} ) + : MessageBoxException{ caption } + , message{ message_ } + {} + ~SimpleMessageBoxException() override; + + SimpleMessageBoxException( const SimpleMessageBoxException& ) = default; + SimpleMessageBoxException &operator = ( + SimpleMessageBoxException && ) PROHIBITED; + + std::unique_ptr< AudacityException > Move() override; + + // Format a default, internationalized error message for this exception. + virtual wxString ErrorMessage() const override; + +private: + wxString message; +}; + struct DefaultDelayedHandlerAction { void operator () (AudacityException *pException) const diff --git a/src/Benchmark.cpp b/src/Benchmark.cpp index 22f78fa2f..e333b046e 100644 --- a/src/Benchmark.cpp +++ b/src/Benchmark.cpp @@ -337,11 +337,14 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event)) // Rememebr the old blocksize, so that we can restore it later. auto oldBlockSize = Sequence::GetMaxDiskBlockSize(); - const auto cleanup = finally([=] - { Sequence::SetMaxDiskBlockSize(oldBlockSize); } - ); Sequence::SetMaxDiskBlockSize(blockSize * 1024); + const auto cleanup = finally( [&] { + Sequence::SetMaxDiskBlockSize(oldBlockSize); + gPrefs->Write(wxT("/GUI/EditClipCanMove"), editClipCanMove); + gPrefs->Flush(); + } ); + wxBusyCursor busy; HoldPrint(true); @@ -426,8 +429,11 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event)) if (mEditDetail) Printf(wxT("Cut: %d - %d \n"), x0 * chunkSize, (x0 + xlen) * chunkSize); - auto tmp = t->Cut(double (x0 * chunkSize), double ((x0 + xlen) * chunkSize)); - if (!tmp) { + Track::Holder tmp; + try { + tmp = t->Cut(double (x0 * chunkSize), double ((x0 + xlen) * chunkSize)); + } + catch (const AudacityException&) { Printf(wxT("Trial %d\n"), z); Printf(wxT("Cut (%d, %d) failed.\n"), (x0 * chunkSize), (x0 + xlen) * chunkSize); @@ -540,7 +546,4 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event)) Printf(wxT("Benchmark completed successfully.\n")); HoldPrint(false); - - gPrefs->Write(wxT("/GUI/EditClipCanMove"), editClipCanMove); - gPrefs->Flush(); } diff --git a/src/FileException.h b/src/FileException.h index ef8f5e506..0d0225fc0 100644 --- a/src/FileException.h +++ b/src/FileException.h @@ -22,7 +22,7 @@ public: const wxString &caption = wxString{}, const wxFileName &renameTarget_ = {}) : MessageBoxException{ caption } - , fileName{ fileName_ }, cause{ cause_ }, renameTarget{ renameTarget_ } + , cause{ cause_ }, fileName{ fileName_ }, renameTarget{ renameTarget_ } {} FileException(FileException&& that) diff --git a/src/LabelTrack.cpp b/src/LabelTrack.cpp index e3cfd720f..a01cfdd2d 100644 --- a/src/LabelTrack.cpp +++ b/src/LabelTrack.cpp @@ -2339,10 +2339,10 @@ bool LabelTrack::Save(wxTextFile * out, bool overwrite) Track::Holder LabelTrack::Cut(double t0, double t1) { auto tmp = Copy(t0, t1); - if (!tmp) - return{}; + if (!Clear(t0, t1)) - return{}; + //THROW_INCONSISTENCY_EXCEPTION + ; return tmp; } @@ -2353,8 +2353,7 @@ Track::Holder LabelTrack::SplitCut(double t0, double t1) // SplitCut() == Copy() + SplitDelete() Track::Holder tmp = Copy(t0, t1); - if (!tmp) - return {}; + if (!SplitDelete(t0, t1)) return {}; diff --git a/src/Menus.cpp b/src/Menus.cpp index 970602c62..9d7a4178d 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -4177,8 +4177,7 @@ void AudacityProject::OnCut() dest = n->Copy(mViewInfo.selectedRegion.t0(), mViewInfo.selectedRegion.t1()); - if (dest) - FinishCopy(n, std::move(dest), newClipboard); + FinishCopy(n, std::move(dest), newClipboard); } n = iter.Next(); } @@ -4309,8 +4308,7 @@ void AudacityProject::OnCopy() if (n->GetSelected()) { auto dest = n->Copy(mViewInfo.selectedRegion.t0(), mViewInfo.selectedRegion.t1()); - if (dest) - FinishCopy(n, std::move(dest), newClipboard); + FinishCopy(n, std::move(dest), newClipboard); } n = iter.Next(); } @@ -4351,31 +4349,29 @@ void AudacityProject::OnPaste() if (c == NULL) return; Track *ff = NULL; - const Track *tmpSrc = NULL; - const Track *tmpC = NULL; - const Track *prev = NULL; + const Track *lastClipBeforeMismatch = NULL; + const Track *mismatchedClip = NULL; + const Track *prevClip = NULL; bool bAdvanceClipboard = true; bool bPastedSomething = false; - bool bTrackTypeMismatch = false; while (n && c) { if (n->GetSelected()) { bAdvanceClipboard = true; - if (tmpC) - c = tmpC; + if (mismatchedClip) + c = mismatchedClip; if (c->GetKind() != n->GetKind()) { - if (!bTrackTypeMismatch) { - tmpSrc = prev; - tmpC = c; + if (!mismatchedClip) { + lastClipBeforeMismatch = prevClip; + mismatchedClip = c; } - bTrackTypeMismatch = true; bAdvanceClipboard = false; - c = tmpSrc; + c = lastClipBeforeMismatch; // If the types still don't match... while (c && c->GetKind() != n->GetKind()) { - prev = c; + prevClip = c; c = clipIter.Next(); } } @@ -4383,7 +4379,7 @@ void AudacityProject::OnPaste() // Handle case where the first track in clipboard // is of different type than the first selected track if (!c) { - c = tmpC; + c = mismatchedClip; while (n && (c->GetKind() != n->GetKind() || !n->GetSelected())) { // Must perform sync-lock adjustment before incrementing n @@ -4399,39 +4395,40 @@ void AudacityProject::OnPaste() // The last possible case for cross-type pastes: triggered when we try to // paste 1+ tracks from one type into 1+ tracks of another type. If // there's a mix of types, this shouldn't run. - if (!c) { - wxMessageBox( - _("Pasting one type of track into another is not allowed."), - _("Error"), wxICON_ERROR, this); - c = n;//so we don't trigger any !c conditions on our way out - break; - } + if (!c) + // Throw, so that any previous changes to the project in this loop + // are discarded. + throw SimpleMessageBoxException{ + _("Pasting one type of track into another is not allowed.") + }; // When trying to copy from stereo to mono track, show error and exit // TODO: Automatically offer user to mix down to mono (unfortunately // this is not easy to implement if (c->GetLinked() && !n->GetLinked()) - { - wxMessageBox( - _("Copying stereo audio into a mono track is not allowed."), - _("Error"), wxICON_ERROR, this); - break; - } + // Throw, so that any previous changes to the project in this loop + // are discarded. + throw SimpleMessageBoxException{ + _("Copying stereo audio into a mono track is not allowed.") + }; if (!ff) ff = n; Maybe locker; if (msClipProject != this && c->GetKind() == Track::Wave) + // Cause duplication of block files on disk, when copy is + // between projects locker.create(static_cast(c)); - if (c->GetKind() == Track::Wave && n && n->GetKind() == Track::Wave) + wxASSERT( n && c ); + if (c->GetKind() == Track::Wave && n->GetKind() == Track::Wave) { bPastedSomething |= ((WaveTrack*)n)->ClearAndPaste(t0, t1, (WaveTrack*)c, true, true); } else if (c->GetKind() == Track::Label && - n && n->GetKind() == Track::Label) + n->GetKind() == Track::Label) { ((LabelTrack *)n)->Clear(t0, t1); @@ -4444,6 +4441,7 @@ void AudacityProject::OnPaste() } else { + n->Clear(t0, t1); bPastedSomething |= n->Paste(t0, c); } @@ -4464,7 +4462,7 @@ void AudacityProject::OnPaste() } if (bAdvanceClipboard){ - prev = c; + prevClip = c; c = clipIter.Next(); } } // if (n->GetSelected()) @@ -4487,7 +4485,8 @@ void AudacityProject::OnPaste() while (n) { if (n->GetSelected() && n->GetKind()==Track::Wave) { - if (c && c->GetKind() == Track::Wave) { + if (c) { + wxASSERT(c->GetKind() == Track::Wave); bPastedSomething |= ((WaveTrack *)n)->ClearAndPaste(t0, t1, (WaveTrack *)c, true, true); } @@ -4598,29 +4597,40 @@ bool AudacityProject::HandlePasteNothingSelected() while (pClip) { Maybe locker; if ((msClipProject != this) && (pClip->GetKind() == Track::Wave)) + // Cause duplication of block files on disk, when copy is + // between projects locker.create(static_cast(pClip)); - Track::Holder pNewTrack; + Track::Holder uNewTrack; + Track *pNewTrack; switch (pClip->GetKind()) { case Track::Wave: { WaveTrack *w = (WaveTrack *)pClip; - pNewTrack = mTrackFactory->NewWaveTrack(w->GetSampleFormat(), w->GetRate()); + uNewTrack = mTrackFactory->NewWaveTrack(w->GetSampleFormat(), w->GetRate()), + pNewTrack = uNewTrack.get(); } break; #ifdef USE_MIDI case Track::Note: - pNewTrack = mTrackFactory->NewNoteTrack(); + uNewTrack = mTrackFactory->NewNoteTrack(), + pNewTrack = uNewTrack.get(); break; #endif // USE_MIDI case Track::Label: - pNewTrack = mTrackFactory->NewLabelTrack(); + uNewTrack = mTrackFactory->NewLabelTrack(), + pNewTrack = uNewTrack.get(); break; - case Track::Time: - pNewTrack = mTrackFactory->NewTimeTrack(); + case Track::Time: { + // Maintain uniqueness of the time track! + pNewTrack = GetTracks()->GetTimeTrack(); + if (!pNewTrack) + uNewTrack = mTrackFactory->NewTimeTrack(), + pNewTrack = uNewTrack.get(); break; + } default: pClip = iterClip.Next(); continue; @@ -4632,10 +4642,13 @@ bool AudacityProject::HandlePasteNothingSelected() wxUnusedVar(bResult); if (!pFirstNewTrack) - pFirstNewTrack = pNewTrack.get(); + pFirstNewTrack = pNewTrack; pNewTrack->SetSelected(true); - FinishCopy(pClip, std::move(pNewTrack), *mTracks); + if (uNewTrack) + FinishCopy(pClip, std::move(uNewTrack), *mTracks); + else + FinishCopy(pClip, pNewTrack); pClip = iterClip.Next(); } @@ -4899,11 +4912,9 @@ void AudacityProject::OnDuplicate() // Make copies not for clipboard but for direct addition to the project auto dest = n->Copy(mViewInfo.selectedRegion.t0(), mViewInfo.selectedRegion.t1(), false); - if (dest) { - dest->Init(*n); - dest->SetOffset(wxMax(mViewInfo.selectedRegion.t0(), n->GetOffset())); - mTracks->Add(std::move(dest)); - } + dest->Init(*n); + dest->SetOffset(wxMax(mViewInfo.selectedRegion.t0(), n->GetOffset())); + mTracks->Add(std::move(dest)); } if (n == l) { @@ -5114,22 +5125,19 @@ void AudacityProject::OnSplit() double sel0 = mViewInfo.selectedRegion.t0(); double sel1 = mViewInfo.selectedRegion.t1(); - dest = NULL; - n->Copy(sel0, sel1, &dest); - if (dest) { - dest->Init(*n); - dest->SetOffset(wxMax(sel0, n->GetOffset())); + dest = n->Copy(sel0, sel1); + dest->Init(*n); + dest->SetOffset(wxMax(sel0, n->GetOffset())); - if (sel1 >= n->GetEndTime()) - n->Clear(sel0, sel1); - else if (sel0 <= n->GetOffset()) { - n->Clear(sel0, sel1); - n->SetOffset(sel1); - } else - n->Silence(sel0, sel1); + if (sel1 >= n->GetEndTime()) + n->Clear(sel0, sel1); + else if (sel0 <= n->GetOffset()) { + n->Clear(sel0, sel1); + n->SetOffset(sel1); + } else + n->Silence(sel0, sel1); - newTracks.Add(dest); - } + newTracks.Add(dest); } n = iter.Next(); } @@ -5175,10 +5183,8 @@ void AudacityProject::OnSplitNew() mViewInfo.selectedRegion.t1()); } #endif - if (dest) { - dest->SetOffset(wxMax(newt0, offset)); - FinishCopy(n, std::move(dest), *mTracks); - } + dest->SetOffset(wxMax(newt0, offset)); + FinishCopy(n, std::move(dest), *mTracks); } if (n == l) { diff --git a/src/NoteTrack.cpp b/src/NoteTrack.cpp index 914a8f6c5..637a614be 100644 --- a/src/NoteTrack.cpp +++ b/src/NoteTrack.cpp @@ -435,7 +435,8 @@ int NoteTrack::GetVisibleChannels() Track::Holder NoteTrack::Cut(double t0, double t1) { if (t1 <= t0) - return{}; + //THROW_INCONSISTENCY_EXCEPTION + ; double len = t1-t0; auto newTrack = std::make_unique(mDirManager); @@ -457,7 +458,8 @@ Track::Holder NoteTrack::Cut(double t0, double t1) Track::Holder NoteTrack::Copy(double t0, double t1, bool) const { if (t1 <= t0) - return{}; + //THROW_INCONSISTENCY_EXCEPTION + ; double len = t1-t0; auto newTrack = std::make_unique(mDirManager); @@ -534,6 +536,18 @@ bool NoteTrack::Paste(double t, const Track *src) return true; } +bool NoteTrack::Silence(double, double) +{ + // to do + return false; +} + +bool NoteTrack::InsertSilence(double, double) +{ + // to do + return false; +} + // Call this function to manipulate the underlying sequence data. This is // NOT the function that handles horizontal dragging. bool NoteTrack::Shift(double t) // t is always seconds diff --git a/src/NoteTrack.h b/src/NoteTrack.h index a314db574..3a62c50e0 100644 --- a/src/NoteTrack.h +++ b/src/NoteTrack.h @@ -105,6 +105,8 @@ class AUDACITY_DLL_API NoteTrack final bool Trim (double t0, double t1) /* not override */; bool Clear(double t0, double t1) override; bool Paste(double t, const Track *src) override; + bool Silence(double t0, double t1) override; + bool InsertSilence(double t, double len) override; bool Shift(double t) /* not override */; #ifdef EXPERIMENTAL_MIDI_OUT diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 726029298..5058573ed 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -438,7 +438,8 @@ std::unique_ptr Sequence::Copy(sampleCount s0, sampleCount s1) const } if (! ConsistencyCheck(wxT("Sequence::Copy()"))) - return {}; + //THROW_INCONSISTENCY_EXCEPTION + ; return dest; } @@ -1114,6 +1115,7 @@ int Sequence::FindBlock(sampleCount pos) const return rval; } +//static bool Sequence::Read(samplePtr buffer, sampleFormat format, const SeqBlock &b, size_t blockRelativeStart, size_t len, bool mayThrow) @@ -1298,7 +1300,7 @@ struct MinMaxSumsq } bool Sequence::GetWaveDisplay(float *min, float *max, float *rms, int* bl, - size_t len, const sampleCount *where) + size_t len, const sampleCount *where) const { wxASSERT(len > 0); const auto s0 = std::max(sampleCount(0), where[0]); @@ -1333,7 +1335,7 @@ bool Sequence::GetWaveDisplay(float *min, float *max, float *rms, int* bl, // Find the range of sample values for this block that // are in the display. - SeqBlock &seqBlock = mBlock[b]; + const SeqBlock &seqBlock = mBlock[b]; const auto start = seqBlock.start; nextSrcX = std::min(s1, start + seqBlock.f->GetLength()); diff --git a/src/Sequence.h b/src/Sequence.h index c458c6119..ede815556 100644 --- a/src/Sequence.h +++ b/src/Sequence.h @@ -101,7 +101,7 @@ class PROFILE_DLL_API Sequence final : public XMLTagHandler{ // bl is negative wherever data are not yet available. // Return true if successful. bool GetWaveDisplay(float *min, float *max, float *rms, int* bl, - size_t len, const sampleCount *where); + size_t len, const sampleCount *where) const; std::unique_ptr Copy(sampleCount s0, sampleCount s1) const; bool Paste(sampleCount s0, const Sequence *src); diff --git a/src/TimeTrack.cpp b/src/TimeTrack.cpp index 6f47af6f3..a552a1bce 100644 --- a/src/TimeTrack.cpp +++ b/src/TimeTrack.cpp @@ -63,20 +63,24 @@ TimeTrack::TimeTrack(const std::shared_ptr &projDirManager, const Zo blankPen.SetColour(214, 214, 214); } -TimeTrack::TimeTrack(const TimeTrack &orig): - Track(orig) +TimeTrack::TimeTrack(const TimeTrack &orig, double *pT0, double *pT1) + : Track(orig) , mZoomInfo(orig.mZoomInfo) { Init(orig); // this copies the TimeTrack metadata (name, range, etc) ///@TODO: Give Envelope:: a copy-constructor instead of this? mEnvelope = std::make_unique(); + mEnvelope->Flatten(1.0); mEnvelope->SetTrackLen(DBL_MAX); SetInterpolateLog(orig.GetInterpolateLog()); // this calls Envelope::SetInterpolateDB - mEnvelope->Flatten(1.0); mEnvelope->SetOffset(0); mEnvelope->SetRange(orig.mEnvelope->GetMinValue(), orig.mEnvelope->GetMaxValue()); - mEnvelope->Paste(0.0, orig.mEnvelope.get()); + if ( pT0 && pT1 ) + // restricted copy + mEnvelope->CopyFrom(orig.mEnvelope.get(), *pT0, *pT1); + else + mEnvelope->Paste(0.0, orig.mEnvelope.get()); ///@TODO: Give Ruler:: a copy-constructor instead of this? mRuler = std::make_unique(); @@ -103,6 +107,45 @@ TimeTrack::~TimeTrack() { } +Track::Holder TimeTrack::Cut( double t0, double t1 ) +{ + auto result = Copy( t0, t1, false ); + Clear( t0, t1 ); + return result; +} + +Track::Holder TimeTrack::Copy( double t0, double t1, bool ) const +{ + auto result = std::make_unique( *this, &t0, &t1 ); + return Track::Holder{ std::move( result ) }; +} + +bool TimeTrack::Clear(double t0, double t1) +{ + mEnvelope->CollapseRegion(t0, t1); + return true; +} + +bool TimeTrack::Paste(double t, const Track * src) +{ + if (src->GetKind() != Track::Time) + return false; + + mEnvelope->Paste(t, static_cast(src)->mEnvelope.get()); + return true; +} + +bool TimeTrack::Silence(double t0, double t1) +{ + return true; +} + +bool TimeTrack::InsertSilence(double t, double len) +{ + mEnvelope->InsertSpace(t, len); + return true; +} + Track::Holder TimeTrack::Duplicate() const { return std::make_unique(*this); diff --git a/src/TimeTrack.h b/src/TimeTrack.h index 03aa29860..16e8743c7 100644 --- a/src/TimeTrack.h +++ b/src/TimeTrack.h @@ -34,11 +34,20 @@ class TimeTrack final : public Track { * Envelope:: and Ruler:: members in order to copy one to the other - unfortunately both lack a * copy-constructor to encapsulate this. * @param orig The original track to copy from + * @param pT0 if not null, then the start of the sub-range to copy + * @param pT1 if not null, then the end of the sub-range to copy */ - TimeTrack(const TimeTrack &orig); + TimeTrack(const TimeTrack &orig, double *pT0 = nullptr, double *pT1 = nullptr); virtual ~TimeTrack(); + Holder Cut( double t0, double t1 ) override; + Holder Copy( double t0, double t1, bool forClipboard ) const override; + bool Clear(double t0, double t1) override; + bool Paste(double t, const Track * src) override; + bool Silence(double t0, double t1) override; + bool InsertSilence(double t, double len) override; + // Identifying the type of track int GetKind() const override { return Time; } diff --git a/src/Track.cpp b/src/Track.cpp index 2978489c0..7b0e5512a 100644 --- a/src/Track.cpp +++ b/src/Track.cpp @@ -309,7 +309,6 @@ bool Track::SyncLockAdjust(double oldT1, double newT1) return true; auto tmp = Cut(oldT1, GetEndTime()); - if (!tmp) return false; bool ret = Paste(newT1, tmp.get()); wxASSERT(ret); // TODO: handle this. diff --git a/src/Track.h b/src/Track.h index 2b74e076d..334efeb41 100644 --- a/src/Track.h +++ b/src/Track.h @@ -206,28 +206,29 @@ class AUDACITY_DLL_API Track /* not final */ : public XMLTagHandler // separate from the Track. const std::shared_ptr &GetDirManager() const { return mDirManager; } - // Create a NEW track and modify this track (or return null for failure) - virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1)) { return{}; } + // Create a NEW track and modify this track + // Return non-NULL or else throw + virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1)) = 0; - // Create a NEW track and don't modify this track (or return null for failure) + // Create a NEW track and don't modify this track + // Return non-NULL or else throw // Note that subclasses may want to distinguish tracks stored in a clipboard // from those stored in a project virtual Holder Copy - (double WXUNUSED(t0), double WXUNUSED(t1), bool forClipboard = true) const - { return{}; } + (double WXUNUSED(t0), double WXUNUSED(t1), bool forClipboard = true) const = 0; // Return true for success - virtual bool Clear(double WXUNUSED(t0), double WXUNUSED(t1)) {return false;} + virtual bool Clear(double WXUNUSED(t0), double WXUNUSED(t1)) = 0; // Return true for success - virtual bool Paste(double WXUNUSED(t), const Track * WXUNUSED(src)) {return false;} + virtual bool Paste(double WXUNUSED(t), const Track * WXUNUSED(src)) = 0; // This can be used to adjust a sync-lock selected track when the selection // is replaced by one of a different length. virtual bool SyncLockAdjust(double oldT1, double newT1); - virtual bool Silence(double WXUNUSED(t0), double WXUNUSED(t1)) {return false;} - virtual bool InsertSilence(double WXUNUSED(t), double WXUNUSED(len)) {return false;} + virtual bool Silence(double WXUNUSED(t0), double WXUNUSED(t1)) = 0; + virtual bool InsertSilence(double WXUNUSED(t), double WXUNUSED(len)) = 0; virtual int GetKind() const { return None; } diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index cfc527576..9d85cc16a 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -1539,6 +1539,7 @@ void WaveClip::WriteXML(XMLWriter &xmlFile) const } bool WaveClip::Paste(double t0, const WaveClip* other) +// STRONG-GUARANTEE { const bool clipNeedsResampling = other->mRate != mRate; const bool clipNeedsNewFormat = @@ -1558,34 +1559,42 @@ bool WaveClip::Paste(double t0, const WaveClip* other) // Force sample formats to match. newClip->ConvertToSampleFormat(mSequence->GetSampleFormat()); pastedClip = newClip.get(); - } else + } + else { // No resampling or format change needed, just use original clip without making a copy pastedClip = other; } + // Paste cut lines contained in pasted clip + WaveClipHolders newCutlines; + for (const auto &cutline: pastedClip->mCutLines) + { + newCutlines.push_back( + make_movable + ( *cutline, mSequence->GetDirManager(), + // Recursively copy cutlines of cutlines. They don't need + // their offsets adjusted. + true)); + newCutlines.back()->Offset(t0 - mOffset); + } + sampleCount s0; TimeToSamplesClip(t0, &s0); bool result = false; + + // Assume STRONG-GUARANTEE from Sequence::Paste if (mSequence->Paste(s0, pastedClip->mSequence.get())) { + // Assume NOFAIL-GUARANTEE in the remaining MarkChanged(); mEnvelope->Paste(s0.as_double()/mRate + mOffset, pastedClip->mEnvelope.get()); mEnvelope->RemoveUnneededPoints(); OffsetCutLines(t0, pastedClip->GetEndTime() - pastedClip->GetStartTime()); - // Paste cut lines contained in pasted clip - for (const auto &cutline: pastedClip->mCutLines) - { - mCutLines.push_back( - make_movable - ( *cutline, mSequence->GetDirManager(), - // Recursively copy cutlines of cutlines. They don't need - // their offsets adjusted. - true)); - mCutLines.back()->Offset(t0 - mOffset); - } + for (auto &holder : newCutlines) + mCutLines.push_back(std::move(holder)); result = true; } @@ -1727,7 +1736,7 @@ bool WaveClip::ClearAndAddCutLine(double t0, double t1) bool WaveClip::FindCutLine(double cutLinePosition, double* cutlineStart /* = NULL */, - double* cutlineEnd /* = NULL */) + double* cutlineEnd /* = NULL */) const { for (const auto &cutline: mCutLines) { @@ -1745,28 +1754,33 @@ bool WaveClip::FindCutLine(double cutLinePosition, } bool WaveClip::ExpandCutLine(double cutLinePosition) +// STRONG-GUARANTEE { - for (auto it = mCutLines.begin(); it != mCutLines.end(); ++it) - { - WaveClip *const cutline = it->get(); - if (fabs(mOffset + cutline->GetOffset() - cutLinePosition) < 0.0001) - { - if (!Paste(mOffset+cutline->GetOffset(), cutline)) - return false; - // Now erase the cutline, - // but be careful to find it again, because Paste above may - // have modified the array of cutlines (if our cutline contained - // another cutline!), invalidating the iterator we had. - auto begin = mCutLines.begin(), end = mCutLines.end(); - it = std::find_if(begin, end, - [=](decltype(*begin) &p){ return p.get() == cutline; }); - if (it != end) - mCutLines.erase(it); // deletes cutline! - else { - wxASSERT(false); - } - return true; + auto end = mCutLines.end(); + auto it = std::find_if( mCutLines.begin(), end, + [&](const WaveClipHolder &cutline) { + return fabs(mOffset + cutline->GetOffset() - cutLinePosition) < 0.0001; + } ); + + if ( it != end ) { + auto cutline = it->get(); + // assume STRONG-GUARANTEE from Paste + if (!Paste(mOffset+cutline->GetOffset(), cutline)) + return false; + // Now erase the cutline, + // but be careful to find it again, because Paste above may + // have modified the array of cutlines (if our cutline contained + // another cutline!), invalidating the iterator we had. + end = mCutLines.end(); + it = std::find_if(mCutLines.begin(), end, + [=](const WaveClipHolder &p) { return p.get() == cutline; }); + if (it != end) + mCutLines.erase(it); // deletes cutline! + else { + // THROW_INCONSISTENCY_EXCEPTION; + wxASSERT(false); } + return true; } return false; @@ -1788,6 +1802,7 @@ bool WaveClip::RemoveCutLine(double cutLinePosition) } void WaveClip::OffsetCutLines(double t0, double len) +// NOFAIL-GUARANTEE { for (const auto &cutLine : mCutLines) { diff --git a/src/WaveClip.h b/src/WaveClip.h index 95fa6103e..44ee6a65f 100644 --- a/src/WaveClip.h +++ b/src/WaveClip.h @@ -141,7 +141,8 @@ public: class WaveClip; // Array of pointers that assume ownership -using WaveClipHolders = std::vector < movable_ptr< WaveClip > >; +using WaveClipHolder = movable_ptr< WaveClip >; +using WaveClipHolders = std::vector < WaveClipHolder >; using WaveClipConstHolders = std::vector < movable_ptr< const WaveClip > >; // Temporary arrays of mere pointers @@ -331,7 +332,7 @@ public: * position could be found. Return false otherwise. */ bool FindCutLine(double cutLinePosition, double* cutLineStart = NULL, - double *cutLineEnd = NULL); + double *cutLineEnd = NULL) const; /** Expand cut line (that is, re-insert audio, then DELETE audio saved in * cut line). Returns true if a cut line could be found and sucessfully diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index 93c641a5a..040c14ca8 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -532,15 +532,14 @@ bool WaveTrack::IsEmpty(double t0, double t1) const Track::Holder WaveTrack::Cut(double t0, double t1) { if (t1 < t0) - return{}; + // THROW_INCONSISTENCY_EXCEPTION + ; auto tmp = Copy(t0, t1); - if (!tmp) - return{}; - if (!Clear(t0, t1)) - return{}; + // THROW_INCONSISTENCY_EXCEPTION + ; return tmp; } @@ -548,14 +547,15 @@ Track::Holder WaveTrack::Cut(double t0, double t1) Track::Holder WaveTrack::SplitCut(double t0, double t1) { if (t1 < t0) - return{}; + //THROW_INCONSISTENCY_EXCEPTION + ; // SplitCut is the same as 'Copy', then 'SplitDelete' auto tmp = Copy(t0, t1); - if (!tmp) - return{}; + if (!SplitDelete(t0, t1)) - return{}; + //THROW_INCONSISTENCY_EXCEPTION + ; return tmp; } @@ -564,14 +564,15 @@ Track::Holder WaveTrack::SplitCut(double t0, double t1) Track::Holder WaveTrack::CutAndAddCutLine(double t0, double t1) { if (t1 < t0) - return {}; + //THROW_INCONSISTENCY_EXCEPTION + ; // Cut is the same as 'Copy', then 'Delete' auto tmp = Copy(t0, t1); - if (!tmp) - return {}; + if (!ClearAndAddCutLine(t0, t1)) - return {}; + //THROW_INCONSISTENCY_EXCEPTION + ; return tmp; } @@ -636,7 +637,8 @@ bool WaveTrack::Trim (double t0, double t1) Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const { if (t1 <= t0) - return{}; + //THROW_INCONSISTENCY_EXCEPTION + ; WaveTrack *newTrack; Track::Holder result @@ -1166,8 +1168,6 @@ bool WaveTrack::SyncLockAdjust(double oldT1, double newT1) gPrefs->Read(wxT("/GUI/EditClipCanMove"), &clipsCanMove); if (clipsCanMove) { auto tmp = Cut (oldT1, GetEndTime() + 1.0/GetRate()); - if (!tmp) - return false; ret = Paste(newT1, tmp.get()); wxASSERT(ret); @@ -1202,6 +1202,7 @@ bool WaveTrack::SyncLockAdjust(double oldT1, double newT1) } bool WaveTrack::Paste(double t0, const Track *src) +// WEAK-GUARANTEE { bool editClipCanMove = true; gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove); @@ -1287,7 +1288,8 @@ bool WaveTrack::Paste(double t0, const Track *src) insideClip = clip.get(); break; } - } else + } + else { // If clips are immovable we also allow prepending to clips if (clip->WithinClip(t0) || @@ -1312,12 +1314,11 @@ bool WaveTrack::Paste(double t0, const Track *src) if (clip->GetStartTime() > insideClip->GetStartTime() && insideClip->GetEndTime() + insertDuration > clip->GetStartTime()) - { - wxMessageBox( - _("There is not enough room available to paste the selection"), - _("Error"), wxICON_STOP); - return false; - } + // STRONG-GUARANTEE in case of this path + // not that it matters. + throw SimpleMessageBoxException{ + _("There is not enough room available to paste the selection") + }; } } @@ -1331,12 +1332,11 @@ bool WaveTrack::Paste(double t0, const Track *src) //printf("paste: multi clip mode!\n"); if (!editClipCanMove && !IsEmpty(t0, t0+insertDuration-1.0/mRate)) - { - wxMessageBox( - _("There is not enough room available to paste the selection"), - _("Error"), wxICON_STOP); - return false; - } + // STRONG-GUARANTEE in case of this path + // not that it matters. + throw SimpleMessageBoxException{ + _("There is not enough room available to paste the selection") + }; for (const auto &clip : other->mClips) { @@ -2447,50 +2447,52 @@ void WaveTrack::UpdateLocationsCache() const // Expand cut line (that is, re-insert audio, then DELETE audio saved in cut line) bool WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart, double* cutlineEnd) +// STRONG-GUARANTEE { bool editClipCanMove = true; gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove); // Find clip which contains this cut line - for (const auto &clip : mClips) + double start = 0, end = 0; + auto pEnd = mClips.end(); + auto pClip = std::find_if( mClips.begin(), pEnd, + [&](const WaveClipHolder &clip) { + return clip->FindCutLine(cutLinePosition, &start, &end); } ); + if (pClip != pEnd) { - double start = 0, end = 0; - - if (clip->FindCutLine(cutLinePosition, &start, &end)) + auto &clip = *pClip; + if (!editClipCanMove) { - if (!editClipCanMove) + // We are not allowed to move the other clips, so see if there + // is enough room to expand the cut line + for (const auto &clip2: mClips) { - // We are not allowed to move the other clips, so see if there - // is enough room to expand the cut line - for (const auto &clip2: mClips) - { - if (clip2->GetStartTime() > clip->GetStartTime() && - clip->GetEndTime() + end - start > clip2->GetStartTime()) - { - wxMessageBox( - _("There is not enough room available to expand the cut line"), - _("Error"), wxICON_STOP); - return false; - } - } - } + if (clip2->GetStartTime() > clip->GetStartTime() && + clip->GetEndTime() + end - start > clip2->GetStartTime()) + // STRONG-GUARANTEE in case of this path + throw SimpleMessageBoxException{ + _("There is not enough room available to expand the cut line") + }; + } + } - if (!clip->ExpandCutLine(cutLinePosition)) - return false; + if (!clip->ExpandCutLine(cutLinePosition)) + return false; - if (cutlineStart) - *cutlineStart = start; - if (cutlineEnd) - *cutlineEnd = end; + // STRONG-GUARANTEE provided that the following gives NOFAIL-GUARANTEE - // Move clips which are to the right of the cut line - if (editClipCanMove) + if (cutlineStart) + *cutlineStart = start; + if (cutlineEnd) + *cutlineEnd = end; + + // Move clips which are to the right of the cut line + if (editClipCanMove) + { + for (const auto &clip2 : mClips) { - for (const auto &clip2 : mClips) - { - if (clip2->GetStartTime() > clip->GetStartTime()) - clip2->Offset(end - start); - } + if (clip2->GetStartTime() > clip->GetStartTime()) + clip2->Offset(end - start); } return true; diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index e0e36d7ed..5b3cb9ec7 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -1278,8 +1278,6 @@ bool Effect::ProcessPass() { bool bGoodResult = true; bool isGenerator = GetType() == EffectTypeGenerate; - bool editClipCanMove; - gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove, true); FloatBuffers inBuffer, outBuffer; ArrayOf inBufPos, outBufPos; diff --git a/src/effects/Equalization.cpp b/src/effects/Equalization.cpp index 954f733fb..6adc84ebf 100644 --- a/src/effects/Equalization.cpp +++ b/src/effects/Equalization.cpp @@ -1173,20 +1173,17 @@ bool EffectEqualization::ProcessOne(int count, WaveTrack * t, //remove the old audio and get the NEW t->Clear(clipStartEndTimes[i].first,clipStartEndTimes[i].second); auto toClipOutput = output->Copy(clipStartEndTimes[i].first-startT+offsetT0,clipStartEndTimes[i].second-startT+offsetT0); - if(toClipOutput) - { - //put the processed audio in - bool bResult = t->Paste(clipStartEndTimes[i].first, toClipOutput.get()); - wxASSERT(bResult); // TO DO: Actually handle this. - wxUnusedVar(bResult); - //if the clip was only partially selected, the Paste will have created a split line. Join is needed to take care of this - //This is not true when the selection is fully contained within one clip (second half of conditional) - if( (clipRealStartEndTimes[i].first != clipStartEndTimes[i].first || - clipRealStartEndTimes[i].second != clipStartEndTimes[i].second) && - !(clipRealStartEndTimes[i].first <= startT && - clipRealStartEndTimes[i].second >= startT+lenT) ) - t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second); - } + //put the processed audio in + bool bResult = t->Paste(clipStartEndTimes[i].first, toClipOutput.get()); + wxASSERT(bResult); // TO DO: Actually handle this. + wxUnusedVar(bResult); + //if the clip was only partially selected, the Paste will have created a split line. Join is needed to take care of this + //This is not true when the selection is fully contained within one clip (second half of conditional) + if( (clipRealStartEndTimes[i].first != clipStartEndTimes[i].first || + clipRealStartEndTimes[i].second != clipStartEndTimes[i].second) && + !(clipRealStartEndTimes[i].first <= startT && + clipRealStartEndTimes[i].second >= startT+lenT) ) + t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second); } } diff --git a/src/effects/Equalization48x.cpp b/src/effects/Equalization48x.cpp index 0a6c2170c..6e5ced834 100644 --- a/src/effects/Equalization48x.cpp +++ b/src/effects/Equalization48x.cpp @@ -558,19 +558,16 @@ bool EffectEqualization48x::ProcessTail(WaveTrack * t, WaveTrack * output, sampl t->Clear(clipStartEndTimes[i].first,clipStartEndTimes[i].second); // output->Copy(clipStartEndTimes[i].first-startT+offsetT0,clipStartEndTimes[i].second-startT+offsetT0, &toClipOutput); auto toClipOutput = output->Copy(clipStartEndTimes[i].first-startT, clipStartEndTimes[i].second-startT); - if(toClipOutput) - { - //put the processed audio in - bool bResult = t->Paste(clipStartEndTimes[i].first, toClipOutput.get()); - wxASSERT(bResult); // TO DO: Actually handle this. - //if the clip was only partially selected, the Paste will have created a split line. Join is needed to take care of this - //This is not true when the selection is fully contained within one clip (second half of conditional) - if( (clipRealStartEndTimes[i].first != clipStartEndTimes[i].first || - clipRealStartEndTimes[i].second != clipStartEndTimes[i].second) && - !(clipRealStartEndTimes[i].first <= startT && - clipRealStartEndTimes[i].second >= startT+lenT) ) - t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second); - } + //put the processed audio in + bool bResult = t->Paste(clipStartEndTimes[i].first, toClipOutput.get()); + wxASSERT(bResult); // TO DO: Actually handle this. + //if the clip was only partially selected, the Paste will have created a split line. Join is needed to take care of this + //This is not true when the selection is fully contained within one clip (second half of conditional) + if( (clipRealStartEndTimes[i].first != clipStartEndTimes[i].first || + clipRealStartEndTimes[i].second != clipStartEndTimes[i].second) && + !(clipRealStartEndTimes[i].first <= startT && + clipRealStartEndTimes[i].second >= startT+lenT) ) + t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second); } return true; }