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

Document guarantees of now void-returning WaveTrack, WaveClip methods

This commit is contained in:
Paul Licameli 2017-04-02 13:15:31 -04:00
commit e633dc0c8b
5 changed files with 292 additions and 198 deletions

View File

@ -2449,8 +2449,20 @@ void AudioIO::StopStream()
// Stop those exceptions here, or else they propagate through too
// many parts of Audacity that are not effects or editing
// operations. GuardedCall ensures that the user sees a warning.
// Also be sure to Flush each track, at the top of the guarded call,
// relying on the guarantee that the track will be left in a flushed
// state, though the append buffer may be lost.
// If the other track operations fail their strong guarantees, then
// the shift for latency correction may be skipped.
GuardedCall<void>( [&] {
WaveTrack* track = mCaptureTracks[i];
// use NOFAIL-GUARANTEE that track is flushed,
// PARTIAL-GUARANTEE that some initial length of the recording
// is saved.
// See comments in FillBuffers().
track->Flush();
if (mPlaybackTracks.size() > 0)
@ -2475,12 +2487,15 @@ void AudioIO::StopStream()
if( appendRecord )
{ // append-recording
if (recordingOffset < 0)
// use STRONG-GUARANTEE
track->Clear(mT0, mT0 - recordingOffset); // cut the latency out
else
// use STRONG-GUARANTEE
track->InsertSilence(mT0, recordingOffset); // put silence in
}
else
{ // recording into a NEW track
// gives NOFAIL-GUARANTEE though we only need STRONG
track->SetOffset(track->GetStartTime() + recordingOffset);
if(track->GetEndTime() < 0.)
{
@ -2488,6 +2503,7 @@ void AudioIO::StopStream()
"Latency Correction setting has caused the recorded audio to be hidden before zero.\nAudacity has brought it back to start at zero.\nYou may have to use the Time Shift Tool (<---> or F5) to drag the track to the right place."),
_("Latency problem"), wxOK);
m.ShowModal();
// gives NOFAIL-GUARANTEE though we only need STRONG
track->SetOffset(0.);
}
}

View File

@ -599,6 +599,7 @@ bool EnvelopeEditor::MouseEvent(const wxMouseEvent & event, wxRect & r,
}
void Envelope::CollapseRegion(double t0, double t1)
// NOFAIL-GUARANTEE
{
// This gets called when somebody clears samples. All of the
// control points within the region disappear and the points
@ -635,6 +636,7 @@ void Envelope::CollapseRegion(double t0, double t1)
// envelope point applies one-past the last actual sample.
// Rather than going to a .5-offset-index, we special case the framing.
void Envelope::Paste(double t0, const Envelope *e)
// NOFAIL-GUARANTEE
{
const bool wasEmpty = (this->mEnv.size() == 0);
@ -828,6 +830,7 @@ Old analysis of cases:
// 'Unneeded' means that the envelope doesn't change by more than
// 'tolerence' without the point being there.
void Envelope::RemoveUnneededPoints(double time, double tolerence)
// NOFAIL-GUARANTEE
{
unsigned int len = mEnv.size();
unsigned int i;
@ -864,6 +867,7 @@ void Envelope::RemoveUnneededPoints(double time, double tolerence)
}
void Envelope::InsertSpace(double t0, double tlen)
// NOFAIL-GUARANTEE
{
unsigned int len = mEnv.size();
unsigned int i;
@ -997,6 +1001,7 @@ int Envelope::Insert(double when, double value)
// Control
void Envelope::SetOffset(double newOffset)
// NOFAIL-GUARANTEE
{
mOffset = newOffset;
}

View File

@ -345,6 +345,7 @@ WaveClip::WaveClip(const WaveClip& orig,
mIsPlaceholder = orig.GetIsPlaceholder();
}
// to do
WaveClip::WaveClip(const WaveClip& orig,
const std::shared_ptr<DirManager> &projDirManager,
bool copyCutlines,
@ -395,6 +396,7 @@ WaveClip::~WaveClip()
}
void WaveClip::SetOffset(double offset)
// NOFAIL-GUARANTEE
{
mOffset = offset;
mEnvelope->SetOffset(mOffset);
@ -406,12 +408,15 @@ bool WaveClip::GetSamples(samplePtr buffer, sampleFormat format,
return mSequence->Get(buffer, format, start, len, mayThrow);
}
bool WaveClip::SetSamples(samplePtr buffer, sampleFormat format,
void WaveClip::SetSamples(samplePtr buffer, sampleFormat format,
sampleCount start, size_t len)
// STRONG-GUARANTEE
{
bool bResult = mSequence->Set(buffer, format, start, len);
// use STRONG-GUARANTEE
mSequence->Set(buffer, format, start, len);
// use NOFAIL-GUARANTEE
MarkChanged();
return bResult;
}
BlockArray* WaveClip::GetSequenceBlockArray()
@ -1345,6 +1350,7 @@ void WaveClip::ConvertToSampleFormat(sampleFormat format)
}
void WaveClip::UpdateEnvelopeTrackLen()
// NOFAIL-GUARANTEE
{
mEnvelope->SetTrackLen((mSequence->GetNumSamples().as_double()) / mRate);
}
@ -1375,9 +1381,12 @@ void WaveClip::GetDisplayRect(wxRect* r)
*r = mDisplayRect;
}
bool WaveClip::Append(samplePtr buffer, sampleFormat format,
void WaveClip::Append(samplePtr buffer, sampleFormat format,
size_t len, unsigned int stride /* = 1 */,
XMLWriter* blockFileLog /*=NULL*/)
// PARTIAL-GUARANTEE in case of exceptions:
// Some prefix (maybe none) of the buffer is appended, and no content already
// flushed to disk is lost.
{
//wxLogDebug(wxT("Append: len=%lli"), (long long) len);
@ -1388,13 +1397,23 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
if (!mAppendBuffer.ptr())
mAppendBuffer.Allocate(maxBlockSize, seqFormat);
auto cleanup = finally( [&] {
// use NOFAIL-GUARANTEE
UpdateEnvelopeTrackLen();
MarkChanged();
} );
for(;;) {
if (mAppendBufferLen >= blockSize) {
bool success =
// flush some previously appended contents
// use STRONG-GUARANTEE
mSequence->Append(mAppendBuffer.ptr(), seqFormat, blockSize,
blockFileLog);
if (!success)
return false;
return;
// use NOFAIL-GUARANTEE for rest of this "if"
memmove(mAppendBuffer.ptr(),
mAppendBuffer.ptr() + blockSize * SAMPLE_SIZE(seqFormat),
(mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat));
@ -1405,6 +1424,7 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
if (len == 0)
break;
// use NOFAIL-GUARANTEE for rest of this "for"
wxASSERT(mAppendBufferLen <= maxBlockSize);
auto toCopy = std::min(len, maxBlockSize - mAppendBufferLen);
@ -1419,38 +1439,43 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
buffer += toCopy * SAMPLE_SIZE(format) * stride;
len -= toCopy;
}
UpdateEnvelopeTrackLen();
MarkChanged();
return true;
}
bool WaveClip::AppendAlias(const wxString &fName, sampleCount start,
void WaveClip::AppendAlias(const wxString &fName, sampleCount start,
size_t len, int channel,bool useOD)
// STRONG-GUARANTEE
{
// use STRONG-GUARANTEE
bool result = mSequence->AppendAlias(fName, start, len, channel,useOD);
// use NOFAIL-GUARANTEE
if (result)
{
UpdateEnvelopeTrackLen();
MarkChanged();
}
return result;
}
bool WaveClip::AppendCoded(const wxString &fName, sampleCount start,
void WaveClip::AppendCoded(const wxString &fName, sampleCount start,
size_t len, int channel, int decodeType)
// STRONG-GUARANTEE
{
// use STRONG-GUARANTEE
bool result = mSequence->AppendCoded(fName, start, len, channel, decodeType);
if (result)
// use NOFAIL-GUARANTEE
if (result)
{
UpdateEnvelopeTrackLen();
MarkChanged();
}
return result;
}
bool WaveClip::Flush()
// NOFAIL-GUARANTEE that the clip will be in a flushed state.
// PARTIAL-GUARANTEE in case of exceptions:
// Some initial portion (maybe none) of the append buffer of the
// clip gets appended; no previously flushed contents are lost.
{
//wxLogDebug(wxT("WaveClip::Flush"));
//wxLogDebug(wxT(" mAppendBufferLen=%lli"), (long long) mAppendBufferLen);
@ -1458,12 +1483,18 @@ bool WaveClip::Flush()
bool success = true;
if (mAppendBufferLen > 0) {
success = mSequence->Append(mAppendBuffer.ptr(), mSequence->GetSampleFormat(), mAppendBufferLen);
if (success) {
auto cleanup = finally( [&] {
// Blow away the append buffer even in case of failure. May lose some
// data but don't leave the track in an un-flushed state.
// Use NOFAIL-GUARANTEE of these steps.
mAppendBufferLen = 0;
UpdateEnvelopeTrackLen();
MarkChanged();
}
} );
success = mSequence->Append(mAppendBuffer.ptr(), mSequence->GetSampleFormat(), mAppendBufferLen);
}
//wxLogDebug(wxT("now sample count %lli"), (long long) mSequence->GetNumSamples());
@ -1538,7 +1569,7 @@ void WaveClip::WriteXML(XMLWriter &xmlFile) const
xmlFile.EndTag(wxT("waveclip"));
}
bool WaveClip::Paste(double t0, const WaveClip* other)
void WaveClip::Paste(double t0, const WaveClip* other)
// STRONG-GUARANTEE
{
const bool clipNeedsResampling = other->mRate != mRate;
@ -1553,8 +1584,7 @@ bool WaveClip::Paste(double t0, const WaveClip* other)
std::make_unique<WaveClip>(*other, mSequence->GetDirManager(), true);
if (clipNeedsResampling)
// The other clip's rate is different from ours, so resample
if (!newClip->Resample(mRate))
return false;
newClip->Resample(mRate);
if (clipNeedsNewFormat)
// Force sample formats to match.
newClip->ConvertToSampleFormat(mSequence->GetSampleFormat());
@ -1582,8 +1612,6 @@ bool WaveClip::Paste(double t0, const WaveClip* other)
sampleCount s0;
TimeToSamplesClip(t0, &s0);
bool result = false;
// Assume STRONG-GUARANTEE from Sequence::Paste
if (mSequence->Paste(s0, pastedClip->mSequence.get()))
{
@ -1596,38 +1624,40 @@ bool WaveClip::Paste(double t0, const WaveClip* other)
for (auto &holder : newCutlines)
mCutLines.push_back(std::move(holder));
result = true;
}
return result;
}
bool WaveClip::InsertSilence(double t, double len)
void WaveClip::InsertSilence(double t, double len)
// STRONG-GUARANTEE
{
sampleCount s0;
TimeToSamplesClip(t, &s0);
auto slen = (sampleCount)floor(len * mRate + 0.5);
// use STRONG-GUARANTEE
if (!GetSequence()->InsertSilence(s0, slen))
{
wxASSERT(false);
return false;
return;
}
// use NOFAIL-GUARANTEE
OffsetCutLines(t, len);
GetEnvelope()->InsertSpace(t, len);
MarkChanged();
return true;
}
bool WaveClip::Clear(double t0, double t1)
void WaveClip::Clear(double t0, double t1)
// STRONG-GUARANTEE
{
sampleCount s0, s1;
TimeToSamplesClip(t0, &s0);
TimeToSamplesClip(t1, &s1);
// use STRONG-GUARANTEE
if (GetSequence()->Delete(s0, s1-s0))
// use NOFAIL-GUARANTEE in the remaining
{
// msmeyer
//
@ -1674,24 +1704,24 @@ bool WaveClip::Clear(double t0, double t1)
Offset(-(GetStartTime() - t0));
MarkChanged();
return true;
}
return false;
}
bool WaveClip::ClearAndAddCutLine(double t0, double t1)
void WaveClip::ClearAndAddCutLine(double t0, double t1)
// WEAK-GUARANTEE
// this WaveClip remains destructible in case of AudacityException.
// But some cutlines may be deleted
{
if (t0 > GetEndTime() || t1 < GetStartTime())
return true; // time out of bounds
return; // time out of bounds
const double clip_t0 = std::max( t0, GetStartTime() );
const double clip_t1 = std::min( t1, GetEndTime() );
auto newClip = make_movable<WaveClip>
auto newClip = make_movable< WaveClip >
(*this, mSequence->GetDirManager(), true, clip_t0, clip_t1);
newClip->SetOffset(clip_t0-mOffset);
newClip->SetOffset( clip_t0 - mOffset );
// Remove cutlines from this clip that were in the selection, shift
// left those that were after the selection
@ -1718,6 +1748,7 @@ bool WaveClip::ClearAndAddCutLine(double t0, double t1)
TimeToSamplesClip(t0, &s0);
TimeToSamplesClip(t1, &s1);
// use WEAK-GUARANTEE
if (GetSequence()->Delete(s0, s1-s0))
{
// Collapse envelope
@ -1728,10 +1759,7 @@ bool WaveClip::ClearAndAddCutLine(double t0, double t1)
MarkChanged();
mCutLines.push_back(std::move(newClip));
return true;
}
else
return false;
}
bool WaveClip::FindCutLine(double cutLinePosition,
@ -1753,7 +1781,7 @@ bool WaveClip::FindCutLine(double cutLinePosition,
return false;
}
bool WaveClip::ExpandCutLine(double cutLinePosition)
void WaveClip::ExpandCutLine(double cutLinePosition)
// STRONG-GUARANTEE
{
auto end = mCutLines.end();
@ -1765,8 +1793,7 @@ bool WaveClip::ExpandCutLine(double cutLinePosition)
if ( it != end ) {
auto cutline = it->get();
// assume STRONG-GUARANTEE from Paste
if (!Paste(mOffset+cutline->GetOffset(), cutline))
return false;
Paste(mOffset+cutline->GetOffset(), cutline);
// 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
@ -1780,10 +1807,7 @@ bool WaveClip::ExpandCutLine(double cutLinePosition)
// THROW_INCONSISTENCY_EXCEPTION;
wxASSERT(false);
}
return true;
}
return false;
}
bool WaveClip::RemoveCutLine(double cutLinePosition)
@ -1839,13 +1863,14 @@ void WaveClip::SetRate(int rate)
MarkChanged();
}
bool WaveClip::Resample(int rate, ProgressDialog *progress)
void WaveClip::Resample(int rate, ProgressDialog *progress)
// STRONG-GUARANTEE
{
// Note: it is not necessary to do this recursively to cutlines.
// They get resampled as needed when they are expanded.
if (rate == mRate)
return true; // Nothing to do
return; // Nothing to do
double factor = (double)rate / (double)mRate;
::Resample resample(true, factor, factor); // constant rate resampling
@ -1905,22 +1930,23 @@ bool WaveClip::Resample(int rate, ProgressDialog *progress)
);
error = (updateResult != ProgressResult::Success);
if (error)
{
break;
}
//throw UserException{};
}
}
if (!error)
if (error)
;
else
{
mSequence = std::move(newSequence);
mRate = rate;
// Use NOFAIL-GUARANTEE in these steps
// Invalidate wave display cache
mWaveCache = std::make_unique<WaveCache>();
// Invalidate the spectrum display cache
mSpecCache = std::make_unique<SpecCache>();
}
return !error;
mSequence = std::move(newSequence);
mRate = rate;
}
}

View File

@ -233,11 +233,12 @@ public:
// Resample clip. This also will set the rate, but without changing
// the length of the clip
bool Resample(int rate, ProgressDialog *progress = NULL);
void Resample(int rate, ProgressDialog *progress = NULL);
void SetOffset(double offset);
double GetOffset() const { return mOffset; }
void Offset(double delta) { SetOffset(GetOffset() + delta); }
void Offset(double delta) // NOFAIL-GUARANTEE
{ SetOffset(GetOffset() + delta); }
double GetStartTime() const;
double GetEndTime() const;
sampleCount GetStartSample() const;
@ -253,7 +254,7 @@ public:
bool GetSamples(samplePtr buffer, sampleFormat format,
sampleCount start, size_t len, bool mayThrow = true) const;
bool SetSamples(samplePtr buffer, sampleFormat format,
void SetSamples(samplePtr buffer, sampleFormat format,
sampleCount start, size_t len);
Envelope* GetEnvelope() { return mEnvelope.get(); }
@ -268,7 +269,8 @@ public:
/** WaveTrack calls this whenever data in the wave clip changes. It is
* called automatically when WaveClip has a chance to know that something
* has changed, like when member functions SetSamples() etc. are called. */
void MarkChanged() { mDirty++; }
void MarkChanged() // NOFAIL-GUARANTEE
{ mDirty++; }
/** Getting high-level data from the for screen display and clipping
* calculations and Contrast */
@ -295,31 +297,31 @@ public:
void UpdateEnvelopeTrackLen();
/// You must call Flush after the last Append
bool Append(samplePtr buffer, sampleFormat format,
void Append(samplePtr buffer, sampleFormat format,
size_t len, unsigned int stride=1,
XMLWriter* blockFileLog = NULL);
/// Flush must be called after last Append
bool Flush();
bool AppendAlias(const wxString &fName, sampleCount start,
void AppendAlias(const wxString &fName, sampleCount start,
size_t len, int channel,bool useOD);
bool AppendCoded(const wxString &fName, sampleCount start,
void AppendCoded(const wxString &fName, sampleCount start,
size_t len, int channel, int decodeType);
/// This name is consistent with WaveTrack::Clear. It performs a "Cut"
/// operation (but without putting the cutted audio to the clipboard)
bool Clear(double t0, double t1);
void Clear(double t0, double t1);
/// Clear, and add cut line that starts at t0 and contains everything until t1.
bool ClearAndAddCutLine(double t0, double t1);
void ClearAndAddCutLine(double t0, double t1);
/// Paste data from other clip, resampling it if not equal rate
bool Paste(double t0, const WaveClip* other);
void Paste(double t0, const WaveClip* other);
/** Insert silence - note that this is an efficient operation for large
* amounts of silence */
bool InsertSilence(double t, double len);
void InsertSilence(double t, double len);
/// Get access to cut lines list
WaveClipHolders &GetCutLines() { return mCutLines; }
@ -337,7 +339,7 @@ public:
/** 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
* expanded, false otherwise */
bool ExpandCutLine(double cutLinePosition);
void ExpandCutLine(double cutLinePosition);
/// Remove cut line, without expanding the audio in it
bool RemoveCutLine(double cutLinePosition);

View File

@ -188,10 +188,12 @@ double WaveTrack::GetOffset() const
}
void WaveTrack::SetOffset(double o)
// NOFAIL-GUARANTEE
{
double delta = o - GetOffset();
for (const auto &clip : mClips)
// assume NOFAIL-GUARANTEE
clip->SetOffset(clip->GetOffset() + delta);
mOffset = o;
@ -543,6 +545,7 @@ Track::Holder WaveTrack::Cut(double t0, double t1)
}
Track::Holder WaveTrack::SplitCut(double t0, double t1)
// STRONG-GUARANTEE
{
if (t1 < t0)
//THROW_INCONSISTENCY_EXCEPTION
@ -579,6 +582,7 @@ Track::Holder WaveTrack::CutAndAddCutLine(double t0, double t1)
//Trim trims within a clip, rather than trimming everything.
//If a bound is outside a clip, it trims everything.
void WaveTrack::Trim (double t0, double t1)
// WEAK-GUARANTEE
{
bool inside0 = false;
bool inside1 = false;
@ -596,15 +600,13 @@ void WaveTrack::Trim (double t0, double t1)
if(t1 > clip->GetStartTime() && t1 < clip->GetEndTime())
{
if (!clip->Clear(t1,clip->GetEndTime()))
return;
clip->Clear(t1,clip->GetEndTime());
inside1 = true;
}
if(t0 > clip->GetStartTime() && t0 < clip->GetEndTime())
{
if (!clip->Clear(clip->GetStartTime(),t0))
return;
clip->Clear(clip->GetStartTime(),t0);
clip->SetOffset(t0);
inside0 = true;
}
@ -682,15 +684,9 @@ Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const
newTrack->GetSampleFormat(),
static_cast<int>(newTrack->GetRate()));
placeholder->SetIsPlaceholder(true);
if ( ! placeholder->InsertSilence(
0, (t1 - t0) - newTrack->GetEndTime()) )
{
}
else
{
placeholder->Offset(newTrack->GetEndTime());
newTrack->mClips.push_back(std::move(placeholder)); // transfer ownership
}
placeholder->InsertSilence(0, (t1 - t0) - newTrack->GetEndTime());
placeholder->Offset(newTrack->GetEndTime());
newTrack->mClips.push_back(std::move(placeholder)); // transfer ownership
}
return result;
@ -702,11 +698,13 @@ Track::Holder WaveTrack::CopyNonconst(double t0, double t1)
}
void WaveTrack::Clear(double t0, double t1)
// STRONG-GUARANTEE
{
HandleClear(t0, t1, false, false);
}
void WaveTrack::ClearAndAddCutLine(double t0, double t1)
// STRONG-GUARANTEE
{
HandleClear(t0, t1, true, false);
}
@ -793,17 +791,22 @@ void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
bool merge, // Whether to remove 'extra' splits
const TimeWarper *effectWarper // How does time change
)
// WEAK-GUARANTEE
// this WaveTrack remains destructible in case of AudacityException.
// But some of its cutline clips may have been destroyed.
{
double dur = wxMin(t1 - t0, src->GetEndTime());
wxArrayDouble splits;
WaveClipHolders cuts;
double dur = std::min(t1 - t0, src->GetEndTime());
// If duration is 0, then it's just a plain paste
if (dur == 0.0) {
// use WEAK-GUARANTEE
Paste(t0, src);
return;
}
wxArrayDouble splits;
WaveClipHolders cuts;
// If provided time warper was NULL, use a default one that does nothing
IdentityTimeWarper localWarper;
const TimeWarper *warper = (effectWarper ? effectWarper : &localWarper);
@ -883,60 +886,63 @@ void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
prev = clip;
}
}
// Refill the array since clips have changed.
clips = SortedClipArray();
{
// Scan the sorted clips to look for the start of the pasted
// region.
WaveClip *prev = nullptr;
for (const auto clip : clips) {
if (prev) {
MergeClips(GetClipIndex(prev), GetClipIndex(clip));
break;
}
if (fabs(t0 - clip->GetEndTime()) < tolerance)
// Merge this clip and the next clip if the start time
// falls within it and this isn't the last clip in the track.
prev = clip;
else
prev = nullptr;
}
}
}
// Restore cut/split lines
if (preserve) {
// Refill the array since clips have changed.
auto clips = SortedClipArray();
// Restore the split lines, transforming the position appropriately
for (const auto split: splits) {
SplitAt(warper->Warp(split));
}
// Restore the saved cut lines, also transforming if time altered
for (const auto &clip : mClips) {
double st;
double et;
st = clip->GetStartTime();
et = clip->GetEndTime();
// Scan the cuts for any that live within this clip
for (auto it = cuts.begin(); it != cuts.end();) {
WaveClip *cut = it->get();
double cs = cut->GetOffset();
// Offset the cut from the start of the clip and add it to
// this clips cutlines.
if (cs >= st && cs <= et) {
cut->SetOffset(warper->Warp(cs) - st);
clip->GetCutLines().push_back( std::move(*it) ); // transfer ownership!
it = cuts.erase(it);
}
else
++it;
{
// Scan the sorted clips to look for the start of the pasted
// region.
WaveClip *prev = nullptr;
for (const auto clip : clips) {
if (prev) {
// It must be that clip is what was pasted and it begins where
// prev ends.
// use WEAK-GUARANTEE
MergeClips(GetClipIndex(prev), GetClipIndex(clip));
break;
}
if (fabs(t0 - clip->GetEndTime()) < tolerance)
// Merge this clip and the next clip if the start time
// falls within it and this isn't the last clip in the track.
prev = clip;
else
prev = nullptr;
}
}
}
// Restore cut/split lines
if (preserve) {
// Restore the split lines, transforming the position appropriately
for (const auto split: splits) {
SplitAt(warper->Warp(split));
}
// Restore the saved cut lines, also transforming if time altered
for (const auto &clip : mClips) {
double st;
double et;
st = clip->GetStartTime();
et = clip->GetEndTime();
// Scan the cuts for any that live within this clip
for (auto it = cuts.begin(); it != cuts.end();) {
WaveClip *cut = it->get();
double cs = cut->GetOffset();
// Offset the cut from the start of the clip and add it to
// this clips cutlines.
if (cs >= st && cs <= et) {
cut->SetOffset(warper->Warp(cs) - st);
clip->GetCutLines().push_back( std::move(*it) ); // transfer ownership!
it = cuts.erase(it);
}
else
++it;
}
}
}
@ -944,6 +950,7 @@ void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
}
void WaveTrack::SplitDelete(double t0, double t1)
// STRONG-GUARANTEE
{
bool addCutLines = false;
bool split = true;
@ -1007,6 +1014,7 @@ void WaveTrack::AddClip(movable_ptr<WaveClip> &&clip)
void WaveTrack::HandleClear(double t0, double t1,
bool addCutLines, bool split)
// STRONG-GUARANTEE
{
if (t1 < t0)
// THROW_INCONSISTENCY_EXCEPTION; // ?
@ -1045,7 +1053,12 @@ void WaveTrack::HandleClear(double t0, double t1,
// Clip data is affected by command
if (addCutLines)
{
clip->ClearAndAddCutLine(t0,t1);
// Don't modify this clip in place, because we want a strong
// guarantee, and might modify another clip
clipsToDelete.push_back( clip.get() );
auto newClip = make_movable<WaveClip>( *clip, mDirManager, true );
newClip->ClearAndAddCutLine( t0, t1 );
clipsToAdd.push_back( std::move( newClip ) );
}
else
{
@ -1054,14 +1067,28 @@ void WaveTrack::HandleClear(double t0, double t1,
if (clip->BeforeClip(t0)) {
// Delete from the left edge
clip->Clear(clip->GetStartTime(), t1);
clip->Offset(t1-clip->GetStartTime());
} else
if (clip->AfterClip(t1)) {
// Don't modify this clip in place, because we want a strong
// guarantee, and might modify another clip
clipsToDelete.push_back( clip.get() );
auto newClip = make_movable<WaveClip>( *clip, mDirManager, true );
newClip->Clear(clip->GetStartTime(), t1);
newClip->Offset(t1-clip->GetStartTime());
clipsToAdd.push_back( std::move( newClip ) );
}
else if (clip->AfterClip(t1)) {
// Delete to right edge
clip->Clear(t0, clip->GetEndTime());
} else
{
// Don't modify this clip in place, because we want a strong
// guarantee, and might modify another clip
clipsToDelete.push_back( clip.get() );
auto newClip = make_movable<WaveClip>( *clip, mDirManager, true );
newClip->Clear(t0, clip->GetEndTime());
clipsToAdd.push_back( std::move( newClip ) );
}
else {
// Delete in the middle of the clip...we actually create two
// NEW clips out of the left and right halves...
@ -1080,7 +1107,14 @@ void WaveTrack::HandleClear(double t0, double t1,
clipsToDelete.push_back(clip.get());
}
}
else { // (We are not doing a split cut)
else {
// (We are not doing a split cut)
// Don't modify this clip in place, because we want a strong
// guarantee, and might modify another clip
clipsToDelete.push_back( clip.get() );
auto newClip = make_movable<WaveClip>( *clip, mDirManager, true );
/* We are going to DELETE part of the clip here. The clip may
* have envelope points, and we need to ensure that the envelope
* outside of the cleared region is not affected. This means
@ -1090,23 +1124,31 @@ void WaveTrack::HandleClear(double t0, double t1,
// clip->Clear keeps points < t0 and >= t1 via Envelope::CollapseRegion
if (clip->GetEnvelope()->GetNumberOfPoints() > 0) { // don't insert env pts if none exist
double val;
if (clip->WithinClip(t0))
{ // start of region within clip
if (clip->WithinClip(t0)) {
// start of region within clip
val = clip->GetEnvelope()->GetValue(t0);
clip->GetEnvelope()->Insert(t0 - clip->GetOffset() - 1.0/clip->GetRate(), val);
}
if (clip->WithinClip(t1))
{ // end of region within clip
newClip->GetEnvelope()->Insert(t0 - clip->GetOffset() - 1.0/clip->GetRate(), val);
}
if (clip->WithinClip(t1)) {
// end of region within clip
val = clip->GetEnvelope()->GetValue(t1);
clip->GetEnvelope()->Insert(t1 - clip->GetOffset(), val);
}
newClip->GetEnvelope()->Insert(t1 - clip->GetOffset(), val);
}
}
if (!clip->Clear(t0,t1))
return;
clip->GetEnvelope()->RemoveUnneededPoints(t0);
newClip->Clear(t0,t1);
newClip->GetEnvelope()->RemoveUnneededPoints(t0);
clipsToAdd.push_back( std::move( newClip ) );
}
}
} else
}
}
// Only now, change the contents of this track
// use NOFAIL-GUARANTEE for the rest
for (const auto &clip : mClips)
{
if (clip->BeforeClip(t1))
{
// Clip is "behind" the region -- offset it unless we're splitting
@ -1359,17 +1401,14 @@ void WaveTrack::Silence(double t0, double t1)
startDelta = 0;
}
if (!clip->GetSequence()->SetSilence(inclipDelta, samplesToCopy))
{
wxASSERT(false); // should always work
return;
}
clip->GetSequence()->SetSilence(inclipDelta, samplesToCopy);
clip->MarkChanged();
}
}
}
void WaveTrack::InsertSilence(double t, double len)
// STRONG-GUARANTEE
{
if (len <= 0)
// THROW_INCONSISTENCY_EXCEPTION; // ?
@ -1378,20 +1417,27 @@ void WaveTrack::InsertSilence(double t, double len)
if (mClips.empty())
{
// Special case if there is no clip yet
WaveClip* clip = CreateClip();
auto clip = make_movable<WaveClip>(mDirManager, mFormat, mRate);
clip->InsertSilence(0, len);
// use NOFAIL-GUARANTEE
mClips.push_back( std::move( clip ) );
return;
}
else {
// Assume at most one clip contains t
const auto end = mClips.end();
const auto it = std::find_if( mClips.begin(), end,
[&](const WaveClipHolder &clip) { return clip->WithinClip(t); } );
for (const auto &clip : mClips)
{
if (clip->BeforeClip(t))
clip->Offset(len);
else if (clip->WithinClip(t))
// use STRONG-GUARANTEE
if (it != end)
it->get()->InsertSilence(t, len);
// use NOFAIL-GUARANTEE
for (const auto &clip : mClips)
{
if (!clip->InsertSilence(t, len)) {
return;
}
if (clip->BeforeClip(t))
clip->Offset(len);
}
}
}
@ -1399,6 +1445,7 @@ void WaveTrack::InsertSilence(double t, double len)
//Performs the opposite of Join
//Analyses selected region for possible Joined clips and disjoins them
void WaveTrack::Disjoin(double t0, double t1)
// WEAK-GUARANTEE
{
auto minSamples = TimeToLongSamples( WAVETRACK_MERGE_POINT_TOLERANCE );
const size_t maxAtOnce = 1048576;
@ -1476,6 +1523,7 @@ void WaveTrack::Disjoin(double t0, double t1)
}
void WaveTrack::Join(double t0, double t1)
// WEAK-GUARANTEE
{
// Merge all WaveClips overlapping selection into one
@ -1517,9 +1565,7 @@ void WaveTrack::Join(double t0, double t1)
}
//printf("Pasting at %.6f\n", t);
bool bResult = newClip->Paste(t, clip);
wxASSERT(bResult); // TO DO: Actually handle this.
wxUnusedVar(bResult);
newClip->Paste(t, clip);
t = newClip->GetEndTime();
auto it = FindClip(mClips, clip);
@ -1530,6 +1576,9 @@ void WaveTrack::Join(double t0, double t1)
void WaveTrack::Append(samplePtr buffer, sampleFormat format,
size_t len, unsigned int stride /* = 1 */,
XMLWriter *blockFileLog /* = NULL */)
// PARTIAL-GUARANTEE in case of exceptions:
// Some prefix (maybe none) of the buffer is appended, and no content already
// flushed to disk is lost.
{
RightmostOrNewClip()->Append(buffer, format, len, stride,
blockFileLog);
@ -1537,12 +1586,14 @@ void WaveTrack::Append(samplePtr buffer, sampleFormat format,
void WaveTrack::AppendAlias(const wxString &fName, sampleCount start,
size_t len, int channel,bool useOD)
// STRONG-GUARANTEE
{
RightmostOrNewClip()->AppendAlias(fName, start, len, channel, useOD);
}
void WaveTrack::AppendCoded(const wxString &fName, sampleCount start,
size_t len, int channel, int decodeType)
// STRONG-GUARANTEE
{
RightmostOrNewClip()->AppendCoded(fName, start, len, channel, decodeType);
}
@ -1616,6 +1667,10 @@ size_t WaveTrack::GetIdealBlockSize()
}
void WaveTrack::Flush()
// NOFAIL-GUARANTEE that the rightmost clip will be in a flushed state.
// PARTIAL-GUARANTEE in case of exceptions:
// Some initial portion (maybe none) of the append buffer of the rightmost
// clip gets appended; no previously saved contents are lost.
{
// After appending, presumably. Do this to the clip that gets appended.
RightmostOrNewClip()->Flush();
@ -2020,6 +2075,7 @@ bool WaveTrack::Get(samplePtr buffer, sampleFormat format,
void WaveTrack::Set(samplePtr buffer, sampleFormat format,
sampleCount start, size_t len)
// WEAK-GUARANTEE
{
for (const auto &clip: mClips)
{
@ -2050,15 +2106,11 @@ void WaveTrack::Set(samplePtr buffer, sampleFormat format,
// samplesToCopy is positive and not more than len
}
if (!clip->SetSamples(
clip->SetSamples(
(samplePtr)(((char*)buffer) +
startDelta.as_size_t() *
SAMPLE_SIZE(format)),
format, inclipDelta, samplesToCopy.as_size_t() ))
{
wxASSERT(false); // should always work
return;
}
format, inclipDelta, samplesToCopy.as_size_t() );
clip->MarkChanged();
}
}
@ -2191,6 +2243,7 @@ WaveClip* WaveTrack::NewestOrNewClip()
}
WaveClip* WaveTrack::RightmostOrNewClip()
// NOFAIL-GUARANTEE
{
if (mClips.empty()) {
WaveClip *clip = CreateClip();
@ -2297,6 +2350,7 @@ bool WaveTrack::CanInsertClip(WaveClip* clip)
}
void WaveTrack::Split( double t0, double t1 )
// WEAK-GUARANTEE
{
SplitAt( t0 );
if( t0 != t1 )
@ -2304,6 +2358,7 @@ void WaveTrack::Split( double t0, double t1 )
}
void WaveTrack::SplitAt(double t)
// WEAK-GUARANTEE
{
for (const auto &c : mClips)
{
@ -2318,14 +2373,8 @@ void WaveTrack::SplitAt(double t)
c->GetEnvelope()->Insert(t - c->GetOffset() - 1.0/c->GetRate(), val); // frame end points
c->GetEnvelope()->Insert(t - c->GetOffset(), val);
auto newClip = make_movable<WaveClip>( *c, mDirManager, true );
if (!c->Clear(t, c->GetEndTime()))
{
return;
}
if (!newClip->Clear(c->GetStartTime(), t))
{
return;
}
c->Clear(t, c->GetEndTime());
newClip->Clear(c->GetStartTime(), t);
//offset the NEW clip by the splitpoint (noting that it is already offset to c->GetStartTime())
sampleCount here = llrint(floor(((t - c->GetStartTime()) * mRate) + 0.5));
@ -2333,6 +2382,7 @@ void WaveTrack::SplitAt(double t)
// This could invalidate the iterators for the loop! But we return
// at once so it's okay
mClips.push_back(std::move(newClip)); // transfer ownership
return;
}
}
}
@ -2435,8 +2485,7 @@ void WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
}
}
if (!clip->ExpandCutLine(cutLinePosition))
return;
clip->ExpandCutLine(cutLinePosition);
// STRONG-GUARANTEE provided that the following gives NOFAIL-GUARANTEE
@ -2467,6 +2516,7 @@ bool WaveTrack::RemoveCutLine(double cutLinePosition)
}
void WaveTrack::MergeClips(int clipidx1, int clipidx2)
// STRONG-GUARANTEE
{
WaveClip* clip1 = GetClipByIndex(clipidx1);
WaveClip* clip2 = GetClipByIndex(clipidx2);
@ -2475,9 +2525,10 @@ void WaveTrack::MergeClips(int clipidx1, int clipidx2)
return; // Don't throw, just do nothing.
// Append data from second clip to first clip
if (!clip1->Paste(clip1->GetEndTime(), clip2))
return;
// use STRONG-GUARANTEE
clip1->Paste(clip1->GetEndTime(), clip2);
// use NOFAIL-GUARANTEE for the rest
// Delete second clip
auto it = FindClip(mClips, clip2);
mClips.erase(it);
@ -2488,13 +2539,7 @@ void WaveTrack::Resample(int rate, ProgressDialog *progress)
// Partial completion may leave clips at differing sample rates!
{
for (const auto &clip : mClips)
if (!clip->Resample(rate, progress))
{
wxLogDebug( wxT("Resampling problem! We're partially resampled") );
// FIXME: The track is now in an inconsistent state since some
// clips are resampled and some are not
return;
}
clip->Resample(rate, progress);
mRate = rate;
}