mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-06 23:02:42 +02:00
Implement the more than weak guarantees needed for recording...
... in WaveClip and WaveTrack, to save as much recording as we can, assuming the strong guarantees that Sequence will give. Also comment that some other WaveTrack methods can give strong guarantee, incidentally to making HandleClear give strong.
This commit is contained in:
parent
e3d4a8dcfe
commit
aa83c4cf29
@ -2449,8 +2449,20 @@ void AudioIO::StopStream()
|
|||||||
// Stop those exceptions here, or else they propagate through too
|
// Stop those exceptions here, or else they propagate through too
|
||||||
// many parts of Audacity that are not effects or editing
|
// many parts of Audacity that are not effects or editing
|
||||||
// operations. GuardedCall ensures that the user sees a warning.
|
// 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>( [&] {
|
GuardedCall<void>( [&] {
|
||||||
WaveTrack* track = mCaptureTracks[i];
|
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();
|
track->Flush();
|
||||||
|
|
||||||
if (mPlaybackTracks.size() > 0)
|
if (mPlaybackTracks.size() > 0)
|
||||||
@ -2475,12 +2487,15 @@ void AudioIO::StopStream()
|
|||||||
if( appendRecord )
|
if( appendRecord )
|
||||||
{ // append-recording
|
{ // append-recording
|
||||||
if (recordingOffset < 0)
|
if (recordingOffset < 0)
|
||||||
|
// use STRONG-GUARANTEE
|
||||||
track->Clear(mT0, mT0 - recordingOffset); // cut the latency out
|
track->Clear(mT0, mT0 - recordingOffset); // cut the latency out
|
||||||
else
|
else
|
||||||
|
// use STRONG-GUARANTEE
|
||||||
track->InsertSilence(mT0, recordingOffset); // put silence in
|
track->InsertSilence(mT0, recordingOffset); // put silence in
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ // recording into a NEW track
|
{ // recording into a NEW track
|
||||||
|
// gives NOFAIL-GUARANTEE though we only need STRONG
|
||||||
track->SetOffset(track->GetStartTime() + recordingOffset);
|
track->SetOffset(track->GetStartTime() + recordingOffset);
|
||||||
if(track->GetEndTime() < 0.)
|
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 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);
|
_("Latency problem"), wxOK);
|
||||||
m.ShowModal();
|
m.ShowModal();
|
||||||
|
// gives NOFAIL-GUARANTEE though we only need STRONG
|
||||||
track->SetOffset(0.);
|
track->SetOffset(0.);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,6 +395,7 @@ WaveClip::~WaveClip()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaveClip::SetOffset(double offset)
|
void WaveClip::SetOffset(double offset)
|
||||||
|
// NOFAIL-GUARANTEE
|
||||||
{
|
{
|
||||||
mOffset = offset;
|
mOffset = offset;
|
||||||
mEnvelope->SetOffset(mOffset);
|
mEnvelope->SetOffset(mOffset);
|
||||||
@ -1345,6 +1346,7 @@ void WaveClip::ConvertToSampleFormat(sampleFormat format)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaveClip::UpdateEnvelopeTrackLen()
|
void WaveClip::UpdateEnvelopeTrackLen()
|
||||||
|
// NOFAIL-GUARANTEE
|
||||||
{
|
{
|
||||||
mEnvelope->SetTrackLen((mSequence->GetNumSamples().as_double()) / mRate);
|
mEnvelope->SetTrackLen((mSequence->GetNumSamples().as_double()) / mRate);
|
||||||
}
|
}
|
||||||
@ -1378,6 +1380,9 @@ void WaveClip::GetDisplayRect(wxRect* r)
|
|||||||
bool WaveClip::Append(samplePtr buffer, sampleFormat format,
|
bool WaveClip::Append(samplePtr buffer, sampleFormat format,
|
||||||
size_t len, unsigned int stride /* = 1 */,
|
size_t len, unsigned int stride /* = 1 */,
|
||||||
XMLWriter* blockFileLog /*=NULL*/)
|
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);
|
//wxLogDebug(wxT("Append: len=%lli"), (long long) len);
|
||||||
|
|
||||||
@ -1388,13 +1393,23 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
|
|||||||
if (!mAppendBuffer.ptr())
|
if (!mAppendBuffer.ptr())
|
||||||
mAppendBuffer.Allocate(maxBlockSize, seqFormat);
|
mAppendBuffer.Allocate(maxBlockSize, seqFormat);
|
||||||
|
|
||||||
|
auto cleanup = finally( [&] {
|
||||||
|
// use NOFAIL-GUARANTEE
|
||||||
|
UpdateEnvelopeTrackLen();
|
||||||
|
MarkChanged();
|
||||||
|
} );
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
if (mAppendBufferLen >= blockSize) {
|
if (mAppendBufferLen >= blockSize) {
|
||||||
bool success =
|
bool success =
|
||||||
|
// flush some previously appended contents
|
||||||
|
// use STRONG-GUARANTEE
|
||||||
mSequence->Append(mAppendBuffer.ptr(), seqFormat, blockSize,
|
mSequence->Append(mAppendBuffer.ptr(), seqFormat, blockSize,
|
||||||
blockFileLog);
|
blockFileLog);
|
||||||
if (!success)
|
if (!success)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// use NOFAIL-GUARANTEE for rest of this "if"
|
||||||
memmove(mAppendBuffer.ptr(),
|
memmove(mAppendBuffer.ptr(),
|
||||||
mAppendBuffer.ptr() + blockSize * SAMPLE_SIZE(seqFormat),
|
mAppendBuffer.ptr() + blockSize * SAMPLE_SIZE(seqFormat),
|
||||||
(mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat));
|
(mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat));
|
||||||
@ -1405,6 +1420,7 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
|
|||||||
if (len == 0)
|
if (len == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// use NOFAIL-GUARANTEE for rest of this "for"
|
||||||
wxASSERT(mAppendBufferLen <= maxBlockSize);
|
wxASSERT(mAppendBufferLen <= maxBlockSize);
|
||||||
auto toCopy = std::min(len, maxBlockSize - mAppendBufferLen);
|
auto toCopy = std::min(len, maxBlockSize - mAppendBufferLen);
|
||||||
|
|
||||||
@ -1420,16 +1436,17 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
|
|||||||
len -= toCopy;
|
len -= toCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateEnvelopeTrackLen();
|
|
||||||
MarkChanged();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WaveClip::AppendAlias(const wxString &fName, sampleCount start,
|
bool WaveClip::AppendAlias(const wxString &fName, sampleCount start,
|
||||||
size_t len, int channel,bool useOD)
|
size_t len, int channel,bool useOD)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
|
// use STRONG-GUARANTEE
|
||||||
bool result = mSequence->AppendAlias(fName, start, len, channel,useOD);
|
bool result = mSequence->AppendAlias(fName, start, len, channel,useOD);
|
||||||
|
|
||||||
|
// use NOFAIL-GUARANTEE
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
UpdateEnvelopeTrackLen();
|
UpdateEnvelopeTrackLen();
|
||||||
@ -1440,9 +1457,13 @@ bool WaveClip::AppendAlias(const wxString &fName, sampleCount start,
|
|||||||
|
|
||||||
bool WaveClip::AppendCoded(const wxString &fName, sampleCount start,
|
bool WaveClip::AppendCoded(const wxString &fName, sampleCount start,
|
||||||
size_t len, int channel, int decodeType)
|
size_t len, int channel, int decodeType)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
|
// use STRONG-GUARANTEE
|
||||||
bool result = mSequence->AppendCoded(fName, start, len, channel, decodeType);
|
bool result = mSequence->AppendCoded(fName, start, len, channel, decodeType);
|
||||||
if (result)
|
|
||||||
|
// use NOFAIL-GUARANTEE
|
||||||
|
if (result)
|
||||||
{
|
{
|
||||||
UpdateEnvelopeTrackLen();
|
UpdateEnvelopeTrackLen();
|
||||||
MarkChanged();
|
MarkChanged();
|
||||||
@ -1451,6 +1472,10 @@ bool WaveClip::AppendCoded(const wxString &fName, sampleCount start,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WaveClip::Flush()
|
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("WaveClip::Flush"));
|
||||||
//wxLogDebug(wxT(" mAppendBufferLen=%lli"), (long long) mAppendBufferLen);
|
//wxLogDebug(wxT(" mAppendBufferLen=%lli"), (long long) mAppendBufferLen);
|
||||||
@ -1458,12 +1483,18 @@ bool WaveClip::Flush()
|
|||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
if (mAppendBufferLen > 0) {
|
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;
|
mAppendBufferLen = 0;
|
||||||
UpdateEnvelopeTrackLen();
|
UpdateEnvelopeTrackLen();
|
||||||
MarkChanged();
|
MarkChanged();
|
||||||
}
|
} );
|
||||||
|
|
||||||
|
success = mSequence->Append(mAppendBuffer.ptr(), mSequence->GetSampleFormat(), mAppendBufferLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
//wxLogDebug(wxT("now sample count %lli"), (long long) mSequence->GetNumSamples());
|
//wxLogDebug(wxT("now sample count %lli"), (long long) mSequence->GetNumSamples());
|
||||||
@ -1603,16 +1634,20 @@ bool WaveClip::Paste(double t0, const WaveClip* other)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WaveClip::InsertSilence(double t, double len)
|
bool WaveClip::InsertSilence(double t, double len)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
sampleCount s0;
|
sampleCount s0;
|
||||||
TimeToSamplesClip(t, &s0);
|
TimeToSamplesClip(t, &s0);
|
||||||
auto slen = (sampleCount)floor(len * mRate + 0.5);
|
auto slen = (sampleCount)floor(len * mRate + 0.5);
|
||||||
|
|
||||||
|
// use STRONG-GUARANTEE
|
||||||
if (!GetSequence()->InsertSilence(s0, slen))
|
if (!GetSequence()->InsertSilence(s0, slen))
|
||||||
{
|
{
|
||||||
wxASSERT(false);
|
wxASSERT(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use NOFAIL-GUARANTEE
|
||||||
OffsetCutLines(t, len);
|
OffsetCutLines(t, len);
|
||||||
GetEnvelope()->InsertSpace(t, len);
|
GetEnvelope()->InsertSpace(t, len);
|
||||||
MarkChanged();
|
MarkChanged();
|
||||||
|
@ -237,7 +237,8 @@ public:
|
|||||||
|
|
||||||
void SetOffset(double offset);
|
void SetOffset(double offset);
|
||||||
double GetOffset() const { return mOffset; }
|
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 GetStartTime() const;
|
||||||
double GetEndTime() const;
|
double GetEndTime() const;
|
||||||
sampleCount GetStartSample() const;
|
sampleCount GetStartSample() const;
|
||||||
@ -268,7 +269,8 @@ public:
|
|||||||
/** WaveTrack calls this whenever data in the wave clip changes. It is
|
/** WaveTrack calls this whenever data in the wave clip changes. It is
|
||||||
* called automatically when WaveClip has a chance to know that something
|
* called automatically when WaveClip has a chance to know that something
|
||||||
* has changed, like when member functions SetSamples() etc. are called. */
|
* 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
|
/** Getting high-level data from the for screen display and clipping
|
||||||
* calculations and Contrast */
|
* calculations and Contrast */
|
||||||
|
@ -188,10 +188,12 @@ double WaveTrack::GetOffset() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaveTrack::SetOffset(double o)
|
void WaveTrack::SetOffset(double o)
|
||||||
|
// NOFAIL-GUARANTEE
|
||||||
{
|
{
|
||||||
double delta = o - GetOffset();
|
double delta = o - GetOffset();
|
||||||
|
|
||||||
for (const auto &clip : mClips)
|
for (const auto &clip : mClips)
|
||||||
|
// assume NOFAIL-GUARANTEE
|
||||||
clip->SetOffset(clip->GetOffset() + delta);
|
clip->SetOffset(clip->GetOffset() + delta);
|
||||||
|
|
||||||
mOffset = o;
|
mOffset = o;
|
||||||
@ -543,6 +545,7 @@ Track::Holder WaveTrack::Cut(double t0, double t1)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Track::Holder WaveTrack::SplitCut(double t0, double t1)
|
Track::Holder WaveTrack::SplitCut(double t0, double t1)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
if (t1 < t0)
|
if (t1 < t0)
|
||||||
//THROW_INCONSISTENCY_EXCEPTION
|
//THROW_INCONSISTENCY_EXCEPTION
|
||||||
@ -703,11 +706,13 @@ Track::Holder WaveTrack::CopyNonconst(double t0, double t1)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaveTrack::Clear(double t0, double t1)
|
void WaveTrack::Clear(double t0, double t1)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
HandleClear(t0, t1, false, false);
|
HandleClear(t0, t1, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaveTrack::ClearAndAddCutLine(double t0, double t1)
|
void WaveTrack::ClearAndAddCutLine(double t0, double t1)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
HandleClear(t0, t1, true, false);
|
HandleClear(t0, t1, true, false);
|
||||||
}
|
}
|
||||||
@ -953,6 +958,7 @@ void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaveTrack::SplitDelete(double t0, double t1)
|
void WaveTrack::SplitDelete(double t0, double t1)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
bool addCutLines = false;
|
bool addCutLines = false;
|
||||||
bool split = true;
|
bool split = true;
|
||||||
@ -1016,6 +1022,7 @@ void WaveTrack::AddClip(movable_ptr<WaveClip> &&clip)
|
|||||||
|
|
||||||
void WaveTrack::HandleClear(double t0, double t1,
|
void WaveTrack::HandleClear(double t0, double t1,
|
||||||
bool addCutLines, bool split)
|
bool addCutLines, bool split)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
if (t1 < t0)
|
if (t1 < t0)
|
||||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||||
@ -1054,7 +1061,12 @@ void WaveTrack::HandleClear(double t0, double t1,
|
|||||||
// Clip data is affected by command
|
// Clip data is affected by command
|
||||||
if (addCutLines)
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -1063,14 +1075,28 @@ void WaveTrack::HandleClear(double t0, double t1,
|
|||||||
|
|
||||||
if (clip->BeforeClip(t0)) {
|
if (clip->BeforeClip(t0)) {
|
||||||
// Delete from the left edge
|
// Delete from the left edge
|
||||||
clip->Clear(clip->GetStartTime(), t1);
|
|
||||||
clip->Offset(t1-clip->GetStartTime());
|
// Don't modify this clip in place, because we want a strong
|
||||||
} else
|
// guarantee, and might modify another clip
|
||||||
if (clip->AfterClip(t1)) {
|
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
|
// 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
|
// Delete in the middle of the clip...we actually create two
|
||||||
// NEW clips out of the left and right halves...
|
// NEW clips out of the left and right halves...
|
||||||
|
|
||||||
@ -1089,7 +1115,14 @@ void WaveTrack::HandleClear(double t0, double t1,
|
|||||||
clipsToDelete.push_back(clip.get());
|
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
|
/* We are going to DELETE part of the clip here. The clip may
|
||||||
* have envelope points, and we need to ensure that the envelope
|
* have envelope points, and we need to ensure that the envelope
|
||||||
* outside of the cleared region is not affected. This means
|
* outside of the cleared region is not affected. This means
|
||||||
@ -1099,23 +1132,32 @@ void WaveTrack::HandleClear(double t0, double t1,
|
|||||||
// clip->Clear keeps points < t0 and >= t1 via Envelope::CollapseRegion
|
// clip->Clear keeps points < t0 and >= t1 via Envelope::CollapseRegion
|
||||||
if (clip->GetEnvelope()->GetNumberOfPoints() > 0) { // don't insert env pts if none exist
|
if (clip->GetEnvelope()->GetNumberOfPoints() > 0) { // don't insert env pts if none exist
|
||||||
double val;
|
double val;
|
||||||
if (clip->WithinClip(t0))
|
if (clip->WithinClip(t0)) {
|
||||||
{ // start of region within clip
|
// start of region within clip
|
||||||
val = clip->GetEnvelope()->GetValue(t0);
|
val = clip->GetEnvelope()->GetValue(t0);
|
||||||
clip->GetEnvelope()->Insert(t0 - clip->GetOffset() - 1.0/clip->GetRate(), val);
|
newClip->GetEnvelope()->Insert(t0 - clip->GetOffset() - 1.0/clip->GetRate(), val);
|
||||||
}
|
}
|
||||||
if (clip->WithinClip(t1))
|
if (clip->WithinClip(t1)) {
|
||||||
{ // end of region within clip
|
// end of region within clip
|
||||||
val = clip->GetEnvelope()->GetValue(t1);
|
val = clip->GetEnvelope()->GetValue(t1);
|
||||||
clip->GetEnvelope()->Insert(t1 - clip->GetOffset(), val);
|
newClip->GetEnvelope()->Insert(t1 - clip->GetOffset(), val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!clip->Clear(t0,t1))
|
if (!newClip->Clear(t0,t1))
|
||||||
return;
|
return;
|
||||||
clip->GetEnvelope()->RemoveUnneededPoints(t0);
|
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))
|
if (clip->BeforeClip(t1))
|
||||||
{
|
{
|
||||||
// Clip is "behind" the region -- offset it unless we're splitting
|
// Clip is "behind" the region -- offset it unless we're splitting
|
||||||
@ -1379,6 +1421,7 @@ void WaveTrack::Silence(double t0, double t1)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaveTrack::InsertSilence(double t, double len)
|
void WaveTrack::InsertSilence(double t, double len)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||||
@ -1387,20 +1430,28 @@ void WaveTrack::InsertSilence(double t, double len)
|
|||||||
if (mClips.empty())
|
if (mClips.empty())
|
||||||
{
|
{
|
||||||
// Special case if there is no clip yet
|
// Special case if there is no clip yet
|
||||||
WaveClip* clip = CreateClip();
|
auto clip = make_movable<WaveClip>(mDirManager, mFormat, mRate);
|
||||||
clip->InsertSilence(0, len);
|
clip->InsertSilence(0, len);
|
||||||
|
// use NOFAIL-GUARANTEE
|
||||||
|
mClips.push_back( std::move( clip ) );
|
||||||
return;
|
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)
|
// use STRONG-GUARANTEE
|
||||||
{
|
if (it != end)
|
||||||
if (clip->BeforeClip(t))
|
if(!it->get()->InsertSilence(t, len))
|
||||||
clip->Offset(len);
|
|
||||||
else if (clip->WithinClip(t))
|
|
||||||
{
|
|
||||||
if (!clip->InsertSilence(t, len)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
// use NOFAIL-GUARANTEE
|
||||||
|
for (const auto &clip : mClips)
|
||||||
|
{
|
||||||
|
if (clip->BeforeClip(t))
|
||||||
|
clip->Offset(len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1541,6 +1592,9 @@ void WaveTrack::Join(double t0, double t1)
|
|||||||
void WaveTrack::Append(samplePtr buffer, sampleFormat format,
|
void WaveTrack::Append(samplePtr buffer, sampleFormat format,
|
||||||
size_t len, unsigned int stride /* = 1 */,
|
size_t len, unsigned int stride /* = 1 */,
|
||||||
XMLWriter *blockFileLog /* = NULL */)
|
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,
|
RightmostOrNewClip()->Append(buffer, format, len, stride,
|
||||||
blockFileLog);
|
blockFileLog);
|
||||||
@ -1548,12 +1602,14 @@ void WaveTrack::Append(samplePtr buffer, sampleFormat format,
|
|||||||
|
|
||||||
void WaveTrack::AppendAlias(const wxString &fName, sampleCount start,
|
void WaveTrack::AppendAlias(const wxString &fName, sampleCount start,
|
||||||
size_t len, int channel,bool useOD)
|
size_t len, int channel,bool useOD)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
RightmostOrNewClip()->AppendAlias(fName, start, len, channel, useOD);
|
RightmostOrNewClip()->AppendAlias(fName, start, len, channel, useOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaveTrack::AppendCoded(const wxString &fName, sampleCount start,
|
void WaveTrack::AppendCoded(const wxString &fName, sampleCount start,
|
||||||
size_t len, int channel, int decodeType)
|
size_t len, int channel, int decodeType)
|
||||||
|
// STRONG-GUARANTEE
|
||||||
{
|
{
|
||||||
RightmostOrNewClip()->AppendCoded(fName, start, len, channel, decodeType);
|
RightmostOrNewClip()->AppendCoded(fName, start, len, channel, decodeType);
|
||||||
}
|
}
|
||||||
@ -1627,6 +1683,10 @@ size_t WaveTrack::GetIdealBlockSize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaveTrack::Flush()
|
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.
|
// After appending, presumably. Do this to the clip that gets appended.
|
||||||
RightmostOrNewClip()->Flush();
|
RightmostOrNewClip()->Flush();
|
||||||
@ -2203,6 +2263,7 @@ WaveClip* WaveTrack::NewestOrNewClip()
|
|||||||
}
|
}
|
||||||
|
|
||||||
WaveClip* WaveTrack::RightmostOrNewClip()
|
WaveClip* WaveTrack::RightmostOrNewClip()
|
||||||
|
// NOFAIL-GUARANTEE
|
||||||
{
|
{
|
||||||
if (mClips.empty()) {
|
if (mClips.empty()) {
|
||||||
WaveClip *clip = CreateClip();
|
WaveClip *clip = CreateClip();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user