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
|
||||
// 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.);
|
||||
}
|
||||
}
|
||||
|
@ -395,6 +395,7 @@ WaveClip::~WaveClip()
|
||||
}
|
||||
|
||||
void WaveClip::SetOffset(double offset)
|
||||
// NOFAIL-GUARANTEE
|
||||
{
|
||||
mOffset = offset;
|
||||
mEnvelope->SetOffset(mOffset);
|
||||
@ -1345,6 +1346,7 @@ void WaveClip::ConvertToSampleFormat(sampleFormat format)
|
||||
}
|
||||
|
||||
void WaveClip::UpdateEnvelopeTrackLen()
|
||||
// NOFAIL-GUARANTEE
|
||||
{
|
||||
mEnvelope->SetTrackLen((mSequence->GetNumSamples().as_double()) / mRate);
|
||||
}
|
||||
@ -1378,6 +1380,9 @@ void WaveClip::GetDisplayRect(wxRect* r)
|
||||
bool 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 +1393,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;
|
||||
|
||||
// use NOFAIL-GUARANTEE for rest of this "if"
|
||||
memmove(mAppendBuffer.ptr(),
|
||||
mAppendBuffer.ptr() + blockSize * SAMPLE_SIZE(seqFormat),
|
||||
(mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat));
|
||||
@ -1405,6 +1420,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);
|
||||
|
||||
@ -1420,16 +1436,17 @@ bool WaveClip::Append(samplePtr buffer, sampleFormat format,
|
||||
len -= toCopy;
|
||||
}
|
||||
|
||||
UpdateEnvelopeTrackLen();
|
||||
MarkChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool 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();
|
||||
@ -1440,9 +1457,13 @@ bool WaveClip::AppendAlias(const wxString &fName, sampleCount start,
|
||||
|
||||
bool 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();
|
||||
@ -1451,6 +1472,10 @@ bool WaveClip::AppendCoded(const wxString &fName, sampleCount start,
|
||||
}
|
||||
|
||||
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());
|
||||
@ -1603,16 +1634,20 @@ bool WaveClip::Paste(double t0, const WaveClip* other)
|
||||
}
|
||||
|
||||
bool 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;
|
||||
}
|
||||
|
||||
// use NOFAIL-GUARANTEE
|
||||
OffsetCutLines(t, len);
|
||||
GetEnvelope()->InsertSpace(t, len);
|
||||
MarkChanged();
|
||||
|
@ -237,7 +237,8 @@ public:
|
||||
|
||||
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;
|
||||
@ -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 */
|
||||
|
@ -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
|
||||
@ -703,11 +706,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);
|
||||
}
|
||||
@ -953,6 +958,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;
|
||||
@ -1016,6 +1022,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; // ?
|
||||
@ -1054,7 +1061,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
|
||||
{
|
||||
@ -1063,14 +1075,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...
|
||||
|
||||
@ -1089,7 +1115,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
|
||||
@ -1099,23 +1132,32 @@ 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))
|
||||
if (!newClip->Clear(t0,t1))
|
||||
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))
|
||||
{
|
||||
// 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)
|
||||
// STRONG-GUARANTEE
|
||||
{
|
||||
if (len <= 0)
|
||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||
@ -1387,20 +1430,28 @@ 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))
|
||||
{
|
||||
if (!clip->InsertSilence(t, len)) {
|
||||
// use STRONG-GUARANTEE
|
||||
if (it != end)
|
||||
if(!it->get()->InsertSilence(t, len))
|
||||
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,
|
||||
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);
|
||||
@ -1548,12 +1602,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);
|
||||
}
|
||||
@ -1627,6 +1683,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();
|
||||
@ -2203,6 +2263,7 @@ WaveClip* WaveTrack::NewestOrNewClip()
|
||||
}
|
||||
|
||||
WaveClip* WaveTrack::RightmostOrNewClip()
|
||||
// NOFAIL-GUARANTEE
|
||||
{
|
||||
if (mClips.empty()) {
|
||||
WaveClip *clip = CreateClip();
|
||||
|
Loading…
x
Reference in New Issue
Block a user