1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-26 17:18:41 +02:00

More exception handling; incidentally implement TimeTrack copy/paste

This commit is contained in:
Paul Licameli 2017-03-31 18:23:47 -04:00
commit 71dd75a596
20 changed files with 352 additions and 225 deletions

View File

@ -50,6 +50,21 @@ MessageBoxException::~MessageBoxException()
wxAtomicDec( sOutstandingMessages ); 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 // This is meant to be invoked via wxEvtHandler::CallAfter
void MessageBoxException::DelayedHandlerAction() void MessageBoxException::DelayedHandlerAction()
{ {

View File

@ -69,6 +69,30 @@ private:
mutable bool moved { false }; 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 struct DefaultDelayedHandlerAction
{ {
void operator () (AudacityException *pException) const void operator () (AudacityException *pException) const

View File

@ -337,11 +337,14 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
// Rememebr the old blocksize, so that we can restore it later. // Rememebr the old blocksize, so that we can restore it later.
auto oldBlockSize = Sequence::GetMaxDiskBlockSize(); auto oldBlockSize = Sequence::GetMaxDiskBlockSize();
const auto cleanup = finally([=]
{ Sequence::SetMaxDiskBlockSize(oldBlockSize); }
);
Sequence::SetMaxDiskBlockSize(blockSize * 1024); Sequence::SetMaxDiskBlockSize(blockSize * 1024);
const auto cleanup = finally( [&] {
Sequence::SetMaxDiskBlockSize(oldBlockSize);
gPrefs->Write(wxT("/GUI/EditClipCanMove"), editClipCanMove);
gPrefs->Flush();
} );
wxBusyCursor busy; wxBusyCursor busy;
HoldPrint(true); HoldPrint(true);
@ -426,8 +429,11 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
if (mEditDetail) if (mEditDetail)
Printf(wxT("Cut: %d - %d \n"), x0 * chunkSize, (x0 + xlen) * chunkSize); Printf(wxT("Cut: %d - %d \n"), x0 * chunkSize, (x0 + xlen) * chunkSize);
auto tmp = t->Cut(double (x0 * chunkSize), double ((x0 + xlen) * chunkSize)); Track::Holder tmp;
if (!tmp) { try {
tmp = t->Cut(double (x0 * chunkSize), double ((x0 + xlen) * chunkSize));
}
catch (const AudacityException&) {
Printf(wxT("Trial %d\n"), z); Printf(wxT("Trial %d\n"), z);
Printf(wxT("Cut (%d, %d) failed.\n"), (x0 * chunkSize), Printf(wxT("Cut (%d, %d) failed.\n"), (x0 * chunkSize),
(x0 + xlen) * chunkSize); (x0 + xlen) * chunkSize);
@ -540,7 +546,4 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
Printf(wxT("Benchmark completed successfully.\n")); Printf(wxT("Benchmark completed successfully.\n"));
HoldPrint(false); HoldPrint(false);
gPrefs->Write(wxT("/GUI/EditClipCanMove"), editClipCanMove);
gPrefs->Flush();
} }

View File

@ -22,7 +22,7 @@ public:
const wxString &caption = wxString{}, const wxString &caption = wxString{},
const wxFileName &renameTarget_ = {}) const wxFileName &renameTarget_ = {})
: MessageBoxException{ caption } : MessageBoxException{ caption }
, fileName{ fileName_ }, cause{ cause_ }, renameTarget{ renameTarget_ } , cause{ cause_ }, fileName{ fileName_ }, renameTarget{ renameTarget_ }
{} {}
FileException(FileException&& that) FileException(FileException&& that)

View File

@ -2339,10 +2339,10 @@ bool LabelTrack::Save(wxTextFile * out, bool overwrite)
Track::Holder LabelTrack::Cut(double t0, double t1) Track::Holder LabelTrack::Cut(double t0, double t1)
{ {
auto tmp = Copy(t0, t1); auto tmp = Copy(t0, t1);
if (!tmp)
return{};
if (!Clear(t0, t1)) if (!Clear(t0, t1))
return{}; //THROW_INCONSISTENCY_EXCEPTION
;
return tmp; return tmp;
} }
@ -2353,8 +2353,7 @@ Track::Holder LabelTrack::SplitCut(double t0, double t1)
// SplitCut() == Copy() + SplitDelete() // SplitCut() == Copy() + SplitDelete()
Track::Holder tmp = Copy(t0, t1); Track::Holder tmp = Copy(t0, t1);
if (!tmp)
return {};
if (!SplitDelete(t0, t1)) if (!SplitDelete(t0, t1))
return {}; return {};

View File

@ -4177,7 +4177,6 @@ void AudacityProject::OnCut()
dest = n->Copy(mViewInfo.selectedRegion.t0(), dest = n->Copy(mViewInfo.selectedRegion.t0(),
mViewInfo.selectedRegion.t1()); mViewInfo.selectedRegion.t1());
if (dest)
FinishCopy(n, std::move(dest), newClipboard); FinishCopy(n, std::move(dest), newClipboard);
} }
n = iter.Next(); n = iter.Next();
@ -4309,7 +4308,6 @@ void AudacityProject::OnCopy()
if (n->GetSelected()) { if (n->GetSelected()) {
auto dest = n->Copy(mViewInfo.selectedRegion.t0(), auto dest = n->Copy(mViewInfo.selectedRegion.t0(),
mViewInfo.selectedRegion.t1()); mViewInfo.selectedRegion.t1());
if (dest)
FinishCopy(n, std::move(dest), newClipboard); FinishCopy(n, std::move(dest), newClipboard);
} }
n = iter.Next(); n = iter.Next();
@ -4351,31 +4349,29 @@ void AudacityProject::OnPaste()
if (c == NULL) if (c == NULL)
return; return;
Track *ff = NULL; Track *ff = NULL;
const Track *tmpSrc = NULL; const Track *lastClipBeforeMismatch = NULL;
const Track *tmpC = NULL; const Track *mismatchedClip = NULL;
const Track *prev = NULL; const Track *prevClip = NULL;
bool bAdvanceClipboard = true; bool bAdvanceClipboard = true;
bool bPastedSomething = false; bool bPastedSomething = false;
bool bTrackTypeMismatch = false;
while (n && c) { while (n && c) {
if (n->GetSelected()) { if (n->GetSelected()) {
bAdvanceClipboard = true; bAdvanceClipboard = true;
if (tmpC) if (mismatchedClip)
c = tmpC; c = mismatchedClip;
if (c->GetKind() != n->GetKind()) { if (c->GetKind() != n->GetKind()) {
if (!bTrackTypeMismatch) { if (!mismatchedClip) {
tmpSrc = prev; lastClipBeforeMismatch = prevClip;
tmpC = c; mismatchedClip = c;
} }
bTrackTypeMismatch = true;
bAdvanceClipboard = false; bAdvanceClipboard = false;
c = tmpSrc; c = lastClipBeforeMismatch;
// If the types still don't match... // If the types still don't match...
while (c && c->GetKind() != n->GetKind()) { while (c && c->GetKind() != n->GetKind()) {
prev = c; prevClip = c;
c = clipIter.Next(); c = clipIter.Next();
} }
} }
@ -4383,7 +4379,7 @@ void AudacityProject::OnPaste()
// Handle case where the first track in clipboard // Handle case where the first track in clipboard
// is of different type than the first selected track // is of different type than the first selected track
if (!c) { if (!c) {
c = tmpC; c = mismatchedClip;
while (n && (c->GetKind() != n->GetKind() || !n->GetSelected())) while (n && (c->GetKind() != n->GetKind() || !n->GetSelected()))
{ {
// Must perform sync-lock adjustment before incrementing n // 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 // 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 // paste 1+ tracks from one type into 1+ tracks of another type. If
// there's a mix of types, this shouldn't run. // there's a mix of types, this shouldn't run.
if (!c) { if (!c)
wxMessageBox( // Throw, so that any previous changes to the project in this loop
_("Pasting one type of track into another is not allowed."), // are discarded.
_("Error"), wxICON_ERROR, this); throw SimpleMessageBoxException{
c = n;//so we don't trigger any !c conditions on our way out _("Pasting one type of track into another is not allowed.")
break; };
}
// When trying to copy from stereo to mono track, show error and exit // When trying to copy from stereo to mono track, show error and exit
// TODO: Automatically offer user to mix down to mono (unfortunately // TODO: Automatically offer user to mix down to mono (unfortunately
// this is not easy to implement // this is not easy to implement
if (c->GetLinked() && !n->GetLinked()) if (c->GetLinked() && !n->GetLinked())
{ // Throw, so that any previous changes to the project in this loop
wxMessageBox( // are discarded.
_("Copying stereo audio into a mono track is not allowed."), throw SimpleMessageBoxException{
_("Error"), wxICON_ERROR, this); _("Copying stereo audio into a mono track is not allowed.")
break; };
}
if (!ff) if (!ff)
ff = n; ff = n;
Maybe<WaveTrack::Locker> locker; Maybe<WaveTrack::Locker> locker;
if (msClipProject != this && c->GetKind() == Track::Wave) if (msClipProject != this && c->GetKind() == Track::Wave)
// Cause duplication of block files on disk, when copy is
// between projects
locker.create(static_cast<const WaveTrack*>(c)); locker.create(static_cast<const WaveTrack*>(c));
if (c->GetKind() == Track::Wave && n && n->GetKind() == Track::Wave) wxASSERT( n && c );
if (c->GetKind() == Track::Wave && n->GetKind() == Track::Wave)
{ {
bPastedSomething |= bPastedSomething |=
((WaveTrack*)n)->ClearAndPaste(t0, t1, (WaveTrack*)c, true, true); ((WaveTrack*)n)->ClearAndPaste(t0, t1, (WaveTrack*)c, true, true);
} }
else if (c->GetKind() == Track::Label && else if (c->GetKind() == Track::Label &&
n && n->GetKind() == Track::Label) n->GetKind() == Track::Label)
{ {
((LabelTrack *)n)->Clear(t0, t1); ((LabelTrack *)n)->Clear(t0, t1);
@ -4444,6 +4441,7 @@ void AudacityProject::OnPaste()
} }
else else
{ {
n->Clear(t0, t1);
bPastedSomething |= n->Paste(t0, c); bPastedSomething |= n->Paste(t0, c);
} }
@ -4464,7 +4462,7 @@ void AudacityProject::OnPaste()
} }
if (bAdvanceClipboard){ if (bAdvanceClipboard){
prev = c; prevClip = c;
c = clipIter.Next(); c = clipIter.Next();
} }
} // if (n->GetSelected()) } // if (n->GetSelected())
@ -4487,7 +4485,8 @@ void AudacityProject::OnPaste()
while (n) { while (n) {
if (n->GetSelected() && n->GetKind()==Track::Wave) { if (n->GetSelected() && n->GetKind()==Track::Wave) {
if (c && c->GetKind() == Track::Wave) { if (c) {
wxASSERT(c->GetKind() == Track::Wave);
bPastedSomething |= bPastedSomething |=
((WaveTrack *)n)->ClearAndPaste(t0, t1, (WaveTrack *)c, true, true); ((WaveTrack *)n)->ClearAndPaste(t0, t1, (WaveTrack *)c, true, true);
} }
@ -4598,29 +4597,40 @@ bool AudacityProject::HandlePasteNothingSelected()
while (pClip) { while (pClip) {
Maybe<WaveTrack::Locker> locker; Maybe<WaveTrack::Locker> locker;
if ((msClipProject != this) && (pClip->GetKind() == Track::Wave)) if ((msClipProject != this) && (pClip->GetKind() == Track::Wave))
// Cause duplication of block files on disk, when copy is
// between projects
locker.create(static_cast<const WaveTrack*>(pClip)); locker.create(static_cast<const WaveTrack*>(pClip));
Track::Holder pNewTrack; Track::Holder uNewTrack;
Track *pNewTrack;
switch (pClip->GetKind()) { switch (pClip->GetKind()) {
case Track::Wave: case Track::Wave:
{ {
WaveTrack *w = (WaveTrack *)pClip; WaveTrack *w = (WaveTrack *)pClip;
pNewTrack = mTrackFactory->NewWaveTrack(w->GetSampleFormat(), w->GetRate()); uNewTrack = mTrackFactory->NewWaveTrack(w->GetSampleFormat(), w->GetRate()),
pNewTrack = uNewTrack.get();
} }
break; break;
#ifdef USE_MIDI #ifdef USE_MIDI
case Track::Note: case Track::Note:
pNewTrack = mTrackFactory->NewNoteTrack(); uNewTrack = mTrackFactory->NewNoteTrack(),
pNewTrack = uNewTrack.get();
break; break;
#endif // USE_MIDI #endif // USE_MIDI
case Track::Label: case Track::Label:
pNewTrack = mTrackFactory->NewLabelTrack(); uNewTrack = mTrackFactory->NewLabelTrack(),
pNewTrack = uNewTrack.get();
break; break;
case Track::Time: case Track::Time: {
pNewTrack = mTrackFactory->NewTimeTrack(); // Maintain uniqueness of the time track!
pNewTrack = GetTracks()->GetTimeTrack();
if (!pNewTrack)
uNewTrack = mTrackFactory->NewTimeTrack(),
pNewTrack = uNewTrack.get();
break; break;
}
default: default:
pClip = iterClip.Next(); pClip = iterClip.Next();
continue; continue;
@ -4632,10 +4642,13 @@ bool AudacityProject::HandlePasteNothingSelected()
wxUnusedVar(bResult); wxUnusedVar(bResult);
if (!pFirstNewTrack) if (!pFirstNewTrack)
pFirstNewTrack = pNewTrack.get(); pFirstNewTrack = pNewTrack;
pNewTrack->SetSelected(true); pNewTrack->SetSelected(true);
FinishCopy(pClip, std::move(pNewTrack), *mTracks); if (uNewTrack)
FinishCopy(pClip, std::move(uNewTrack), *mTracks);
else
FinishCopy(pClip, pNewTrack);
pClip = iterClip.Next(); pClip = iterClip.Next();
} }
@ -4899,12 +4912,10 @@ void AudacityProject::OnDuplicate()
// Make copies not for clipboard but for direct addition to the project // Make copies not for clipboard but for direct addition to the project
auto dest = n->Copy(mViewInfo.selectedRegion.t0(), auto dest = n->Copy(mViewInfo.selectedRegion.t0(),
mViewInfo.selectedRegion.t1(), false); mViewInfo.selectedRegion.t1(), false);
if (dest) {
dest->Init(*n); dest->Init(*n);
dest->SetOffset(wxMax(mViewInfo.selectedRegion.t0(), n->GetOffset())); dest->SetOffset(wxMax(mViewInfo.selectedRegion.t0(), n->GetOffset()));
mTracks->Add(std::move(dest)); mTracks->Add(std::move(dest));
} }
}
if (n == l) { if (n == l) {
break; break;
@ -5114,9 +5125,7 @@ void AudacityProject::OnSplit()
double sel0 = mViewInfo.selectedRegion.t0(); double sel0 = mViewInfo.selectedRegion.t0();
double sel1 = mViewInfo.selectedRegion.t1(); double sel1 = mViewInfo.selectedRegion.t1();
dest = NULL; dest = n->Copy(sel0, sel1);
n->Copy(sel0, sel1, &dest);
if (dest) {
dest->Init(*n); dest->Init(*n);
dest->SetOffset(wxMax(sel0, n->GetOffset())); dest->SetOffset(wxMax(sel0, n->GetOffset()));
@ -5130,7 +5139,6 @@ void AudacityProject::OnSplit()
newTracks.Add(dest); newTracks.Add(dest);
} }
}
n = iter.Next(); n = iter.Next();
} }
@ -5175,11 +5183,9 @@ void AudacityProject::OnSplitNew()
mViewInfo.selectedRegion.t1()); mViewInfo.selectedRegion.t1());
} }
#endif #endif
if (dest) {
dest->SetOffset(wxMax(newt0, offset)); dest->SetOffset(wxMax(newt0, offset));
FinishCopy(n, std::move(dest), *mTracks); FinishCopy(n, std::move(dest), *mTracks);
} }
}
if (n == l) { if (n == l) {
break; break;

View File

@ -435,7 +435,8 @@ int NoteTrack::GetVisibleChannels()
Track::Holder NoteTrack::Cut(double t0, double t1) Track::Holder NoteTrack::Cut(double t0, double t1)
{ {
if (t1 <= t0) if (t1 <= t0)
return{}; //THROW_INCONSISTENCY_EXCEPTION
;
double len = t1-t0; double len = t1-t0;
auto newTrack = std::make_unique<NoteTrack>(mDirManager); auto newTrack = std::make_unique<NoteTrack>(mDirManager);
@ -457,7 +458,8 @@ Track::Holder NoteTrack::Cut(double t0, double t1)
Track::Holder NoteTrack::Copy(double t0, double t1, bool) const Track::Holder NoteTrack::Copy(double t0, double t1, bool) const
{ {
if (t1 <= t0) if (t1 <= t0)
return{}; //THROW_INCONSISTENCY_EXCEPTION
;
double len = t1-t0; double len = t1-t0;
auto newTrack = std::make_unique<NoteTrack>(mDirManager); auto newTrack = std::make_unique<NoteTrack>(mDirManager);
@ -534,6 +536,18 @@ bool NoteTrack::Paste(double t, const Track *src)
return true; 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 // Call this function to manipulate the underlying sequence data. This is
// NOT the function that handles horizontal dragging. // NOT the function that handles horizontal dragging.
bool NoteTrack::Shift(double t) // t is always seconds bool NoteTrack::Shift(double t) // t is always seconds

View File

@ -105,6 +105,8 @@ class AUDACITY_DLL_API NoteTrack final
bool Trim (double t0, double t1) /* not override */; bool Trim (double t0, double t1) /* not override */;
bool Clear(double t0, double t1) override; bool Clear(double t0, double t1) override;
bool Paste(double t, const Track *src) 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 */; bool Shift(double t) /* not override */;
#ifdef EXPERIMENTAL_MIDI_OUT #ifdef EXPERIMENTAL_MIDI_OUT

View File

@ -438,7 +438,8 @@ std::unique_ptr<Sequence> Sequence::Copy(sampleCount s0, sampleCount s1) const
} }
if (! ConsistencyCheck(wxT("Sequence::Copy()"))) if (! ConsistencyCheck(wxT("Sequence::Copy()")))
return {}; //THROW_INCONSISTENCY_EXCEPTION
;
return dest; return dest;
} }
@ -1114,6 +1115,7 @@ int Sequence::FindBlock(sampleCount pos) const
return rval; return rval;
} }
//static
bool Sequence::Read(samplePtr buffer, sampleFormat format, bool Sequence::Read(samplePtr buffer, sampleFormat format,
const SeqBlock &b, size_t blockRelativeStart, size_t len, const SeqBlock &b, size_t blockRelativeStart, size_t len,
bool mayThrow) bool mayThrow)
@ -1298,7 +1300,7 @@ struct MinMaxSumsq
} }
bool Sequence::GetWaveDisplay(float *min, float *max, float *rms, int* bl, 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); wxASSERT(len > 0);
const auto s0 = std::max(sampleCount(0), where[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 // Find the range of sample values for this block that
// are in the display. // are in the display.
SeqBlock &seqBlock = mBlock[b]; const SeqBlock &seqBlock = mBlock[b];
const auto start = seqBlock.start; const auto start = seqBlock.start;
nextSrcX = std::min(s1, start + seqBlock.f->GetLength()); nextSrcX = std::min(s1, start + seqBlock.f->GetLength());

View File

@ -101,7 +101,7 @@ class PROFILE_DLL_API Sequence final : public XMLTagHandler{
// bl is negative wherever data are not yet available. // bl is negative wherever data are not yet available.
// Return true if successful. // Return true if successful.
bool GetWaveDisplay(float *min, float *max, float *rms, int* bl, 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<Sequence> Copy(sampleCount s0, sampleCount s1) const; std::unique_ptr<Sequence> Copy(sampleCount s0, sampleCount s1) const;
bool Paste(sampleCount s0, const Sequence *src); bool Paste(sampleCount s0, const Sequence *src);

View File

@ -63,19 +63,23 @@ TimeTrack::TimeTrack(const std::shared_ptr<DirManager> &projDirManager, const Zo
blankPen.SetColour(214, 214, 214); blankPen.SetColour(214, 214, 214);
} }
TimeTrack::TimeTrack(const TimeTrack &orig): TimeTrack::TimeTrack(const TimeTrack &orig, double *pT0, double *pT1)
Track(orig) : Track(orig)
, mZoomInfo(orig.mZoomInfo) , mZoomInfo(orig.mZoomInfo)
{ {
Init(orig); // this copies the TimeTrack metadata (name, range, etc) Init(orig); // this copies the TimeTrack metadata (name, range, etc)
///@TODO: Give Envelope:: a copy-constructor instead of this? ///@TODO: Give Envelope:: a copy-constructor instead of this?
mEnvelope = std::make_unique<Envelope>(); mEnvelope = std::make_unique<Envelope>();
mEnvelope->Flatten(1.0);
mEnvelope->SetTrackLen(DBL_MAX); mEnvelope->SetTrackLen(DBL_MAX);
SetInterpolateLog(orig.GetInterpolateLog()); // this calls Envelope::SetInterpolateDB SetInterpolateLog(orig.GetInterpolateLog()); // this calls Envelope::SetInterpolateDB
mEnvelope->Flatten(1.0);
mEnvelope->SetOffset(0); mEnvelope->SetOffset(0);
mEnvelope->SetRange(orig.mEnvelope->GetMinValue(), orig.mEnvelope->GetMaxValue()); mEnvelope->SetRange(orig.mEnvelope->GetMinValue(), orig.mEnvelope->GetMaxValue());
if ( pT0 && pT1 )
// restricted copy
mEnvelope->CopyFrom(orig.mEnvelope.get(), *pT0, *pT1);
else
mEnvelope->Paste(0.0, orig.mEnvelope.get()); mEnvelope->Paste(0.0, orig.mEnvelope.get());
///@TODO: Give Ruler:: a copy-constructor instead of this? ///@TODO: Give Ruler:: a copy-constructor instead of this?
@ -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<TimeTrack>( *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<const TimeTrack*>(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 Track::Holder TimeTrack::Duplicate() const
{ {
return std::make_unique<TimeTrack>(*this); return std::make_unique<TimeTrack>(*this);

View File

@ -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 * Envelope:: and Ruler:: members in order to copy one to the other - unfortunately both lack a
* copy-constructor to encapsulate this. * copy-constructor to encapsulate this.
* @param orig The original track to copy from * @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(); 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 // Identifying the type of track
int GetKind() const override { return Time; } int GetKind() const override { return Time; }

View File

@ -309,7 +309,6 @@ bool Track::SyncLockAdjust(double oldT1, double newT1)
return true; return true;
auto tmp = Cut(oldT1, GetEndTime()); auto tmp = Cut(oldT1, GetEndTime());
if (!tmp) return false;
bool ret = Paste(newT1, tmp.get()); bool ret = Paste(newT1, tmp.get());
wxASSERT(ret); // TODO: handle this. wxASSERT(ret); // TODO: handle this.

View File

@ -206,28 +206,29 @@ class AUDACITY_DLL_API Track /* not final */ : public XMLTagHandler
// separate from the Track. // separate from the Track.
const std::shared_ptr<DirManager> &GetDirManager() const { return mDirManager; } const std::shared_ptr<DirManager> &GetDirManager() const { return mDirManager; }
// Create a NEW track and modify this track (or return null for failure) // Create a NEW track and modify this track
virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1)) { return{}; } // 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 // Note that subclasses may want to distinguish tracks stored in a clipboard
// from those stored in a project // from those stored in a project
virtual Holder Copy virtual Holder Copy
(double WXUNUSED(t0), double WXUNUSED(t1), bool forClipboard = true) const (double WXUNUSED(t0), double WXUNUSED(t1), bool forClipboard = true) const = 0;
{ return{}; }
// Return true for success // 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 // 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 // This can be used to adjust a sync-lock selected track when the selection
// is replaced by one of a different length. // is replaced by one of a different length.
virtual bool SyncLockAdjust(double oldT1, double newT1); virtual bool SyncLockAdjust(double oldT1, double newT1);
virtual bool Silence(double WXUNUSED(t0), double WXUNUSED(t1)) {return false;} virtual bool Silence(double WXUNUSED(t0), double WXUNUSED(t1)) = 0;
virtual bool InsertSilence(double WXUNUSED(t), double WXUNUSED(len)) {return false;} virtual bool InsertSilence(double WXUNUSED(t), double WXUNUSED(len)) = 0;
virtual int GetKind() const { return None; } virtual int GetKind() const { return None; }

View File

@ -1539,6 +1539,7 @@ void WaveClip::WriteXML(XMLWriter &xmlFile) const
} }
bool WaveClip::Paste(double t0, const WaveClip* other) bool WaveClip::Paste(double t0, const WaveClip* other)
// STRONG-GUARANTEE
{ {
const bool clipNeedsResampling = other->mRate != mRate; const bool clipNeedsResampling = other->mRate != mRate;
const bool clipNeedsNewFormat = const bool clipNeedsNewFormat =
@ -1558,34 +1559,42 @@ bool WaveClip::Paste(double t0, const WaveClip* other)
// Force sample formats to match. // Force sample formats to match.
newClip->ConvertToSampleFormat(mSequence->GetSampleFormat()); newClip->ConvertToSampleFormat(mSequence->GetSampleFormat());
pastedClip = newClip.get(); pastedClip = newClip.get();
} else }
else
{ {
// No resampling or format change needed, just use original clip without making a copy // No resampling or format change needed, just use original clip without making a copy
pastedClip = other; pastedClip = other;
} }
// Paste cut lines contained in pasted clip
WaveClipHolders newCutlines;
for (const auto &cutline: pastedClip->mCutLines)
{
newCutlines.push_back(
make_movable<WaveClip>
( *cutline, mSequence->GetDirManager(),
// Recursively copy cutlines of cutlines. They don't need
// their offsets adjusted.
true));
newCutlines.back()->Offset(t0 - mOffset);
}
sampleCount s0; sampleCount s0;
TimeToSamplesClip(t0, &s0); TimeToSamplesClip(t0, &s0);
bool result = false; bool result = false;
// Assume STRONG-GUARANTEE from Sequence::Paste
if (mSequence->Paste(s0, pastedClip->mSequence.get())) if (mSequence->Paste(s0, pastedClip->mSequence.get()))
{ {
// Assume NOFAIL-GUARANTEE in the remaining
MarkChanged(); MarkChanged();
mEnvelope->Paste(s0.as_double()/mRate + mOffset, pastedClip->mEnvelope.get()); mEnvelope->Paste(s0.as_double()/mRate + mOffset, pastedClip->mEnvelope.get());
mEnvelope->RemoveUnneededPoints(); mEnvelope->RemoveUnneededPoints();
OffsetCutLines(t0, pastedClip->GetEndTime() - pastedClip->GetStartTime()); OffsetCutLines(t0, pastedClip->GetEndTime() - pastedClip->GetStartTime());
// Paste cut lines contained in pasted clip for (auto &holder : newCutlines)
for (const auto &cutline: pastedClip->mCutLines) mCutLines.push_back(std::move(holder));
{
mCutLines.push_back(
make_movable<WaveClip>
( *cutline, mSequence->GetDirManager(),
// Recursively copy cutlines of cutlines. They don't need
// their offsets adjusted.
true));
mCutLines.back()->Offset(t0 - mOffset);
}
result = true; result = true;
} }
@ -1727,7 +1736,7 @@ bool WaveClip::ClearAndAddCutLine(double t0, double t1)
bool WaveClip::FindCutLine(double cutLinePosition, bool WaveClip::FindCutLine(double cutLinePosition,
double* cutlineStart /* = NULL */, double* cutlineStart /* = NULL */,
double* cutlineEnd /* = NULL */) double* cutlineEnd /* = NULL */) const
{ {
for (const auto &cutline: mCutLines) for (const auto &cutline: mCutLines)
{ {
@ -1745,29 +1754,34 @@ bool WaveClip::FindCutLine(double cutLinePosition,
} }
bool WaveClip::ExpandCutLine(double cutLinePosition) bool WaveClip::ExpandCutLine(double cutLinePosition)
// STRONG-GUARANTEE
{ {
for (auto it = mCutLines.begin(); it != mCutLines.end(); ++it) auto end = mCutLines.end();
{ auto it = std::find_if( mCutLines.begin(), end,
WaveClip *const cutline = it->get(); [&](const WaveClipHolder &cutline) {
if (fabs(mOffset + cutline->GetOffset() - cutLinePosition) < 0.0001) 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)) if (!Paste(mOffset+cutline->GetOffset(), cutline))
return false; return false;
// Now erase the cutline, // Now erase the cutline,
// but be careful to find it again, because Paste above may // but be careful to find it again, because Paste above may
// have modified the array of cutlines (if our cutline contained // have modified the array of cutlines (if our cutline contained
// another cutline!), invalidating the iterator we had. // another cutline!), invalidating the iterator we had.
auto begin = mCutLines.begin(), end = mCutLines.end(); end = mCutLines.end();
it = std::find_if(begin, end, it = std::find_if(mCutLines.begin(), end,
[=](decltype(*begin) &p){ return p.get() == cutline; }); [=](const WaveClipHolder &p) { return p.get() == cutline; });
if (it != end) if (it != end)
mCutLines.erase(it); // deletes cutline! mCutLines.erase(it); // deletes cutline!
else { else {
// THROW_INCONSISTENCY_EXCEPTION;
wxASSERT(false); wxASSERT(false);
} }
return true; return true;
} }
}
return false; return false;
} }
@ -1788,6 +1802,7 @@ bool WaveClip::RemoveCutLine(double cutLinePosition)
} }
void WaveClip::OffsetCutLines(double t0, double len) void WaveClip::OffsetCutLines(double t0, double len)
// NOFAIL-GUARANTEE
{ {
for (const auto &cutLine : mCutLines) for (const auto &cutLine : mCutLines)
{ {

View File

@ -141,7 +141,8 @@ public:
class WaveClip; class WaveClip;
// Array of pointers that assume ownership // 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 > >; using WaveClipConstHolders = std::vector < movable_ptr< const WaveClip > >;
// Temporary arrays of mere pointers // Temporary arrays of mere pointers
@ -331,7 +332,7 @@ public:
* position could be found. Return false otherwise. */ * position could be found. Return false otherwise. */
bool FindCutLine(double cutLinePosition, bool FindCutLine(double cutLinePosition,
double* cutLineStart = NULL, double* cutLineStart = NULL,
double *cutLineEnd = NULL); double *cutLineEnd = NULL) const;
/** Expand cut line (that is, re-insert audio, then DELETE audio saved in /** 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 * cut line). Returns true if a cut line could be found and sucessfully

View File

@ -532,15 +532,14 @@ bool WaveTrack::IsEmpty(double t0, double t1) const
Track::Holder WaveTrack::Cut(double t0, double t1) Track::Holder WaveTrack::Cut(double t0, double t1)
{ {
if (t1 < t0) if (t1 < t0)
return{}; // THROW_INCONSISTENCY_EXCEPTION
;
auto tmp = Copy(t0, t1); auto tmp = Copy(t0, t1);
if (!tmp)
return{};
if (!Clear(t0, t1)) if (!Clear(t0, t1))
return{}; // THROW_INCONSISTENCY_EXCEPTION
;
return tmp; return tmp;
} }
@ -548,14 +547,15 @@ Track::Holder WaveTrack::Cut(double t0, double t1)
Track::Holder WaveTrack::SplitCut(double t0, double t1) Track::Holder WaveTrack::SplitCut(double t0, double t1)
{ {
if (t1 < t0) if (t1 < t0)
return{}; //THROW_INCONSISTENCY_EXCEPTION
;
// SplitCut is the same as 'Copy', then 'SplitDelete' // SplitCut is the same as 'Copy', then 'SplitDelete'
auto tmp = Copy(t0, t1); auto tmp = Copy(t0, t1);
if (!tmp)
return{};
if (!SplitDelete(t0, t1)) if (!SplitDelete(t0, t1))
return{}; //THROW_INCONSISTENCY_EXCEPTION
;
return tmp; return tmp;
} }
@ -564,14 +564,15 @@ Track::Holder WaveTrack::SplitCut(double t0, double t1)
Track::Holder WaveTrack::CutAndAddCutLine(double t0, double t1) Track::Holder WaveTrack::CutAndAddCutLine(double t0, double t1)
{ {
if (t1 < t0) if (t1 < t0)
return {}; //THROW_INCONSISTENCY_EXCEPTION
;
// Cut is the same as 'Copy', then 'Delete' // Cut is the same as 'Copy', then 'Delete'
auto tmp = Copy(t0, t1); auto tmp = Copy(t0, t1);
if (!tmp)
return {};
if (!ClearAndAddCutLine(t0, t1)) if (!ClearAndAddCutLine(t0, t1))
return {}; //THROW_INCONSISTENCY_EXCEPTION
;
return tmp; return tmp;
} }
@ -636,7 +637,8 @@ bool WaveTrack::Trim (double t0, double t1)
Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const
{ {
if (t1 <= t0) if (t1 <= t0)
return{}; //THROW_INCONSISTENCY_EXCEPTION
;
WaveTrack *newTrack; WaveTrack *newTrack;
Track::Holder result Track::Holder result
@ -1166,8 +1168,6 @@ bool WaveTrack::SyncLockAdjust(double oldT1, double newT1)
gPrefs->Read(wxT("/GUI/EditClipCanMove"), &clipsCanMove); gPrefs->Read(wxT("/GUI/EditClipCanMove"), &clipsCanMove);
if (clipsCanMove) { if (clipsCanMove) {
auto tmp = Cut (oldT1, GetEndTime() + 1.0/GetRate()); auto tmp = Cut (oldT1, GetEndTime() + 1.0/GetRate());
if (!tmp)
return false;
ret = Paste(newT1, tmp.get()); ret = Paste(newT1, tmp.get());
wxASSERT(ret); wxASSERT(ret);
@ -1202,6 +1202,7 @@ bool WaveTrack::SyncLockAdjust(double oldT1, double newT1)
} }
bool WaveTrack::Paste(double t0, const Track *src) bool WaveTrack::Paste(double t0, const Track *src)
// WEAK-GUARANTEE
{ {
bool editClipCanMove = true; bool editClipCanMove = true;
gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove); gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove);
@ -1287,7 +1288,8 @@ bool WaveTrack::Paste(double t0, const Track *src)
insideClip = clip.get(); insideClip = clip.get();
break; break;
} }
} else }
else
{ {
// If clips are immovable we also allow prepending to clips // If clips are immovable we also allow prepending to clips
if (clip->WithinClip(t0) || if (clip->WithinClip(t0) ||
@ -1312,12 +1314,11 @@ bool WaveTrack::Paste(double t0, const Track *src)
if (clip->GetStartTime() > insideClip->GetStartTime() && if (clip->GetStartTime() > insideClip->GetStartTime() &&
insideClip->GetEndTime() + insertDuration > insideClip->GetEndTime() + insertDuration >
clip->GetStartTime()) clip->GetStartTime())
{ // STRONG-GUARANTEE in case of this path
wxMessageBox( // not that it matters.
_("There is not enough room available to paste the selection"), throw SimpleMessageBoxException{
_("Error"), wxICON_STOP); _("There is not enough room available to paste the selection")
return false; };
}
} }
} }
@ -1331,12 +1332,11 @@ bool WaveTrack::Paste(double t0, const Track *src)
//printf("paste: multi clip mode!\n"); //printf("paste: multi clip mode!\n");
if (!editClipCanMove && !IsEmpty(t0, t0+insertDuration-1.0/mRate)) if (!editClipCanMove && !IsEmpty(t0, t0+insertDuration-1.0/mRate))
{ // STRONG-GUARANTEE in case of this path
wxMessageBox( // not that it matters.
_("There is not enough room available to paste the selection"), throw SimpleMessageBoxException{
_("Error"), wxICON_STOP); _("There is not enough room available to paste the selection")
return false; };
}
for (const auto &clip : other->mClips) for (const auto &clip : other->mClips)
{ {
@ -2447,17 +2447,20 @@ void WaveTrack::UpdateLocationsCache() const
// Expand cut line (that is, re-insert audio, then DELETE audio saved in cut line) // Expand cut line (that is, re-insert audio, then DELETE audio saved in cut line)
bool WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart, bool WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
double* cutlineEnd) double* cutlineEnd)
// STRONG-GUARANTEE
{ {
bool editClipCanMove = true; bool editClipCanMove = true;
gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove); gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove);
// Find clip which contains this cut line // Find clip which contains this cut line
for (const auto &clip : mClips)
{
double start = 0, end = 0; double start = 0, end = 0;
auto pEnd = mClips.end();
if (clip->FindCutLine(cutLinePosition, &start, &end)) auto pClip = std::find_if( mClips.begin(), pEnd,
[&](const WaveClipHolder &clip) {
return clip->FindCutLine(cutLinePosition, &start, &end); } );
if (pClip != pEnd)
{ {
auto &clip = *pClip;
if (!editClipCanMove) if (!editClipCanMove)
{ {
// We are not allowed to move the other clips, so see if there // We are not allowed to move the other clips, so see if there
@ -2466,18 +2469,18 @@ bool WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
{ {
if (clip2->GetStartTime() > clip->GetStartTime() && if (clip2->GetStartTime() > clip->GetStartTime() &&
clip->GetEndTime() + end - start > clip2->GetStartTime()) clip->GetEndTime() + end - start > clip2->GetStartTime())
{ // STRONG-GUARANTEE in case of this path
wxMessageBox( throw SimpleMessageBoxException{
_("There is not enough room available to expand the cut line"), _("There is not enough room available to expand the cut line")
_("Error"), wxICON_STOP); };
return false;
}
} }
} }
if (!clip->ExpandCutLine(cutLinePosition)) if (!clip->ExpandCutLine(cutLinePosition))
return false; return false;
// STRONG-GUARANTEE provided that the following gives NOFAIL-GUARANTEE
if (cutlineStart) if (cutlineStart)
*cutlineStart = start; *cutlineStart = start;
if (cutlineEnd) if (cutlineEnd)
@ -2491,7 +2494,6 @@ bool WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
if (clip2->GetStartTime() > clip->GetStartTime()) if (clip2->GetStartTime() > clip->GetStartTime())
clip2->Offset(end - start); clip2->Offset(end - start);
} }
}
return true; return true;
} }

View File

@ -1278,8 +1278,6 @@ bool Effect::ProcessPass()
{ {
bool bGoodResult = true; bool bGoodResult = true;
bool isGenerator = GetType() == EffectTypeGenerate; bool isGenerator = GetType() == EffectTypeGenerate;
bool editClipCanMove;
gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove, true);
FloatBuffers inBuffer, outBuffer; FloatBuffers inBuffer, outBuffer;
ArrayOf<float *> inBufPos, outBufPos; ArrayOf<float *> inBufPos, outBufPos;

View File

@ -1173,8 +1173,6 @@ bool EffectEqualization::ProcessOne(int count, WaveTrack * t,
//remove the old audio and get the NEW //remove the old audio and get the NEW
t->Clear(clipStartEndTimes[i].first,clipStartEndTimes[i].second); t->Clear(clipStartEndTimes[i].first,clipStartEndTimes[i].second);
auto toClipOutput = output->Copy(clipStartEndTimes[i].first-startT+offsetT0,clipStartEndTimes[i].second-startT+offsetT0); auto toClipOutput = output->Copy(clipStartEndTimes[i].first-startT+offsetT0,clipStartEndTimes[i].second-startT+offsetT0);
if(toClipOutput)
{
//put the processed audio in //put the processed audio in
bool bResult = t->Paste(clipStartEndTimes[i].first, toClipOutput.get()); bool bResult = t->Paste(clipStartEndTimes[i].first, toClipOutput.get());
wxASSERT(bResult); // TO DO: Actually handle this. wxASSERT(bResult); // TO DO: Actually handle this.
@ -1188,7 +1186,6 @@ bool EffectEqualization::ProcessOne(int count, WaveTrack * t,
t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second); t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second);
} }
} }
}
return bLoopSuccess; return bLoopSuccess;
} }

View File

@ -558,8 +558,6 @@ bool EffectEqualization48x::ProcessTail(WaveTrack * t, WaveTrack * output, sampl
t->Clear(clipStartEndTimes[i].first,clipStartEndTimes[i].second); t->Clear(clipStartEndTimes[i].first,clipStartEndTimes[i].second);
// output->Copy(clipStartEndTimes[i].first-startT+offsetT0,clipStartEndTimes[i].second-startT+offsetT0, &toClipOutput); // 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); auto toClipOutput = output->Copy(clipStartEndTimes[i].first-startT, clipStartEndTimes[i].second-startT);
if(toClipOutput)
{
//put the processed audio in //put the processed audio in
bool bResult = t->Paste(clipStartEndTimes[i].first, toClipOutput.get()); bool bResult = t->Paste(clipStartEndTimes[i].first, toClipOutput.get());
wxASSERT(bResult); // TO DO: Actually handle this. wxASSERT(bResult); // TO DO: Actually handle this.
@ -571,7 +569,6 @@ bool EffectEqualization48x::ProcessTail(WaveTrack * t, WaveTrack * output, sampl
clipRealStartEndTimes[i].second >= startT+lenT) ) clipRealStartEndTimes[i].second >= startT+lenT) )
t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second); t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second);
} }
}
return true; return true;
} }