1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-10 17:11:17 +02:00

Wave track duplication: don't append empty clips, do copy cutlines

This commit is contained in:
Paul Licameli 2017-03-09 09:24:51 -05:00
commit 9119d703ce
13 changed files with 159 additions and 128 deletions

View File

@ -2361,7 +2361,7 @@ Track::Holder LabelTrack::SplitCut(double t0, double t1)
} }
#endif #endif
Track::Holder LabelTrack::Copy(double t0, double t1) const Track::Holder LabelTrack::Copy(double t0, double t1, bool) const
{ {
auto tmp = std::make_unique<LabelTrack>(GetDirManager()); auto tmp = std::make_unique<LabelTrack>(GetDirManager());
const auto lt = static_cast<LabelTrack*>(tmp.get()); const auto lt = static_cast<LabelTrack*>(tmp.get());

View File

@ -155,11 +155,7 @@ class AUDACITY_DLL_API LabelTrack final : public Track
#endif #endif
Track::Holder Cut (double t0, double t1) override; Track::Holder Cut (double t0, double t1) override;
// JKC Do not add the const modifier to Copy(), Clear() Track::Holder Copy (double t0, double t1, bool forClipboard = true) const override;
// or Paste() because then it
// is no longer recognised as a virtual function matching the
// one in Track.
Track::Holder Copy (double t0, double t1) const 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 Repeat(double t0, double t1, int n); bool Repeat(double t0, double t1, int n);

View File

@ -4789,8 +4789,9 @@ void AudacityProject::OnDuplicate()
while (n) { while (n) {
if (n->GetSelected()) { if (n->GetSelected()) {
// 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()); mViewInfo.selectedRegion.t1(), false);
if (dest) { if (dest) {
dest->Init(*n); dest->Init(*n);
dest->SetOffset(wxMax(mViewInfo.selectedRegion.t0(), n->GetOffset())); dest->SetOffset(wxMax(mViewInfo.selectedRegion.t0(), n->GetOffset()));

View File

@ -454,7 +454,7 @@ Track::Holder NoteTrack::Cut(double t0, double t1)
return std::move(newTrack); return std::move(newTrack);
} }
Track::Holder NoteTrack::Copy(double t0, double t1) const Track::Holder NoteTrack::Copy(double t0, double t1, bool) const
{ {
if (t1 <= t0) if (t1 <= t0)
return{}; return{};

View File

@ -91,7 +91,7 @@ class AUDACITY_DLL_API NoteTrack final : public Track {
// High-level editing // High-level editing
Track::Holder Cut (double t0, double t1) override; Track::Holder Cut (double t0, double t1) override;
Track::Holder Copy (double t0, double t1) const override; Track::Holder Copy (double t0, double t1, bool forClipboard = true) const override;
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;

View File

@ -4905,6 +4905,7 @@ void AudacityProject::EditClipboardByLabel( EditDestFunction action )
if( n->GetKind() == Track::Wave && ( allTracks || n->GetSelected() ) ) if( n->GetKind() == Track::Wave && ( allTracks || n->GetSelected() ) )
{ {
WaveTrack *wt = ( WaveTrack* )n; WaveTrack *wt = ( WaveTrack* )n;
// This track accumulates the needed clips, right to left:
Track::Holder merged; Track::Holder merged;
for( int i = (int)regions.size() - 1; i >= 0; i-- ) for( int i = (int)regions.size() - 1; i >= 0; i-- )
{ {
@ -4923,6 +4924,11 @@ void AudacityProject::EditClipboardByLabel( EditDestFunction action )
merged->Offset( merged->Offset(
regions.at(i + 1).start - region.end); regions.at(i + 1).start - region.end);
// dest may have a placeholder clip at the end that is
// removed when pasting, which is okay because we proceed
// right to left. Any placeholder already in merged is kept.
// Only the rightmost placeholder is important in the final
// result.
bool bResult = merged->Paste( 0.0 , dest.get() ); bool bResult = merged->Paste( 0.0 , dest.get() );
wxASSERT(bResult); // TO DO: Actually handle this. wxASSERT(bResult); // TO DO: Actually handle this.
wxUnusedVar(bResult); wxUnusedVar(bResult);

View File

@ -385,12 +385,10 @@ bool Sequence::GetRMS(sampleCount start, sampleCount len,
return true; return true;
} }
bool Sequence::Copy(sampleCount s0, sampleCount s1, std::unique_ptr<Sequence> &dest) const std::unique_ptr<Sequence> Sequence::Copy(sampleCount s0, sampleCount s1) const
{ {
dest.reset();
if (s0 >= s1 || s0 >= mNumSamples || s1 < 0) if (s0 >= s1 || s0 >= mNumSamples || s1 < 0)
return false; return {};
int numBlocks = mBlock.size(); int numBlocks = mBlock.size();
@ -402,7 +400,7 @@ bool Sequence::Copy(sampleCount s0, sampleCount s1, std::unique_ptr<Sequence> &d
wxUnusedVar(numBlocks); wxUnusedVar(numBlocks);
wxASSERT(b0 <= b1); wxASSERT(b0 <= b1);
dest = std::make_unique<Sequence>(mDirManager, mSampleFormat); auto dest = std::make_unique<Sequence>(mDirManager, mSampleFormat);
dest->mBlock.reserve(b1 - b0 + 1); dest->mBlock.reserve(b1 - b0 + 1);
SampleBuffer buffer(mMaxSamples, mSampleFormat); SampleBuffer buffer(mMaxSamples, mSampleFormat);
@ -445,7 +443,10 @@ bool Sequence::Copy(sampleCount s0, sampleCount s1, std::unique_ptr<Sequence> &d
dest->AppendBlock(block); // Increase ref count or duplicate file dest->AppendBlock(block); // Increase ref count or duplicate file
} }
return ConsistencyCheck(wxT("Sequence::Copy()")); if (! ConsistencyCheck(wxT("Sequence::Copy()")))
return {};
return dest;
} }
namespace { namespace {

View File

@ -103,7 +103,7 @@ class PROFILE_DLL_API Sequence final : public XMLTagHandler{
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);
bool Copy(sampleCount s0, sampleCount s1, std::unique_ptr<Sequence> &dest) 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);
size_t GetIdealAppendLen() const; size_t GetIdealAppendLen() const;

View File

@ -216,7 +216,11 @@ class AUDACITY_DLL_API Track /* not final */ : public XMLTagHandler
virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1)) { return{}; } virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1)) { return{}; }
// 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 (or return null for failure)
virtual Holder Copy(double WXUNUSED(t0), double WXUNUSED(t1)) const { return{}; } // Note that subclasses may want to distinguish tracks stored in a clipboard
// from those stored in a project
virtual Holder Copy
(double WXUNUSED(t0), double WXUNUSED(t1), bool forClipboard = true) const
{ return{}; }
// 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)) {return false;}

View File

@ -300,21 +300,22 @@ static void ComputeSpectrumUsingRealFFTf
} }
} }
WaveClip::WaveClip(const std::shared_ptr<DirManager> &projDirManager, sampleFormat format, int rate) WaveClip::WaveClip(const std::shared_ptr<DirManager> &projDirManager,
sampleFormat format, int rate)
{ {
mOffset = 0;
mRate = rate; mRate = rate;
mSequence = std::make_unique<Sequence>(projDirManager, format); mSequence = std::make_unique<Sequence>(projDirManager, format);
mEnvelope = std::make_unique<Envelope>(); mEnvelope = std::make_unique<Envelope>();
mWaveCache = std::make_unique<WaveCache>(); mWaveCache = std::make_unique<WaveCache>();
mSpecCache = std::make_unique<SpecCache>(); mSpecCache = std::make_unique<SpecCache>();
mSpecPxCache = std::make_unique<SpecPxCache>(1); mSpecPxCache = std::make_unique<SpecPxCache>(1);
mAppendBufferLen = 0;
mDirty = 0;
mIsPlaceholder = false;
} }
WaveClip::WaveClip(const WaveClip& orig, const std::shared_ptr<DirManager> &projDirManager) WaveClip::WaveClip(const WaveClip& orig,
const std::shared_ptr<DirManager> &projDirManager,
bool copyCutlines)
{ {
// essentially a copy constructor - but you must pass in the // essentially a copy constructor - but you must pass in the
// current project's DirManager, because we might be copying // current project's DirManager, because we might be copying
@ -323,22 +324,69 @@ WaveClip::WaveClip(const WaveClip& orig, const std::shared_ptr<DirManager> &proj
mOffset = orig.mOffset; mOffset = orig.mOffset;
mRate = orig.mRate; mRate = orig.mRate;
mSequence = std::make_unique<Sequence>(*orig.mSequence, projDirManager); mSequence = std::make_unique<Sequence>(*orig.mSequence, projDirManager);
mEnvelope = std::make_unique<Envelope>(); mEnvelope = std::make_unique<Envelope>();
mEnvelope->Paste(0.0, orig.mEnvelope.get()); mEnvelope->Paste(0.0, orig.mEnvelope.get());
mEnvelope->SetOffset(orig.GetOffset()); mEnvelope->SetOffset(orig.GetOffset());
mEnvelope->SetTrackLen((orig.mSequence->GetNumSamples().as_double()) / orig.mRate); mEnvelope->SetTrackLen((orig.mSequence->GetNumSamples().as_double()) / orig.mRate);
mWaveCache = std::make_unique<WaveCache>(); mWaveCache = std::make_unique<WaveCache>();
mSpecCache = std::make_unique<SpecCache>(); mSpecCache = std::make_unique<SpecCache>();
mSpecPxCache = std::make_unique<SpecPxCache>(1); mSpecPxCache = std::make_unique<SpecPxCache>(1);
for (const auto &clip: orig.mCutLines) if ( copyCutlines )
mCutLines.push_back(make_movable<WaveClip>(*clip, projDirManager)); for (const auto &clip: orig.mCutLines)
mCutLines.push_back
( make_movable<WaveClip>( *clip, projDirManager, true ) );
mAppendBufferLen = 0;
mDirty = 0;
mIsPlaceholder = orig.GetIsPlaceholder(); mIsPlaceholder = orig.GetIsPlaceholder();
} }
WaveClip::WaveClip(const WaveClip& orig,
const std::shared_ptr<DirManager> &projDirManager,
bool copyCutlines,
double t0, double t1)
{
// Copy only a range of the other WaveClip
mOffset = orig.mOffset;
mRate = orig.mRate;
mWaveCache = std::make_unique<WaveCache>();
mSpecCache = std::make_unique<SpecCache>();
mSpecPxCache = std::make_unique<SpecPxCache>(1);
mIsPlaceholder = orig.GetIsPlaceholder();
sampleCount s0, s1;
orig.TimeToSamplesClip(t0, &s0);
orig.TimeToSamplesClip(t1, &s1);
mSequence = orig.mSequence->Copy(s0, s1);
mEnvelope = std::make_unique<Envelope>();
mEnvelope->CopyFrom(orig.mEnvelope.get(),
mOffset + s0.as_double()/mRate,
mOffset + s1.as_double()/mRate);
if ( copyCutlines )
// Copy cutline clips that fall in the range
for (const auto &ppClip : orig.mCutLines)
{
const WaveClip* clip = ppClip.get();
double cutlinePosition = orig.mOffset + clip->GetOffset();
if (cutlinePosition >= t0 && cutlinePosition <= t1)
{
auto newCutLine =
make_movable< WaveClip >( *clip, projDirManager, true );
newCutLine->SetOffset( cutlinePosition - t0 );
mCutLines.push_back(std::move(newCutLine));
}
}
}
WaveClip::~WaveClip() WaveClip::~WaveClip()
{ {
} }
@ -1277,6 +1325,9 @@ bool WaveClip::GetRMS(float *rms, double t0,
void WaveClip::ConvertToSampleFormat(sampleFormat format) void WaveClip::ConvertToSampleFormat(sampleFormat format)
{ {
// Note: it is not necessary to do this recursively to cutlines.
// They get converted as needed when they are expanded.
bool bChanged; bool bChanged;
bool bResult = mSequence->ConvertToSampleFormat(format, &bChanged); bool bResult = mSequence->ConvertToSampleFormat(format, &bChanged);
if (bResult && bChanged) if (bResult && bChanged)
@ -1477,30 +1528,6 @@ void WaveClip::WriteXML(XMLWriter &xmlFile) const
xmlFile.EndTag(wxT("waveclip")); xmlFile.EndTag(wxT("waveclip"));
} }
bool WaveClip::CreateFromCopy(double t0, double t1, const WaveClip* other)
{
sampleCount s0, s1;
other->TimeToSamplesClip(t0, &s0);
other->TimeToSamplesClip(t1, &s1);
std::unique_ptr<Sequence> oldSequence = std::move(mSequence);
if (!other->mSequence->Copy(s0, s1, mSequence))
{
mSequence = std::move(oldSequence);
return false;
}
mEnvelope = std::make_unique<Envelope>();
mEnvelope->CopyFrom(other->mEnvelope.get(),
mOffset + s0.as_double()/mRate,
mOffset + s1.as_double()/mRate);
MarkChanged();
return true;
}
bool WaveClip::Paste(double t0, const WaveClip* other) bool WaveClip::Paste(double t0, const WaveClip* other)
{ {
const bool clipNeedsResampling = other->mRate != mRate; const bool clipNeedsResampling = other->mRate != mRate;
@ -1512,7 +1539,7 @@ bool WaveClip::Paste(double t0, const WaveClip* other)
if (clipNeedsResampling || clipNeedsNewFormat) if (clipNeedsResampling || clipNeedsNewFormat)
{ {
newClip = newClip =
std::make_unique<WaveClip>(*other, mSequence->GetDirManager()); std::make_unique<WaveClip>(*other, mSequence->GetDirManager(), true);
if (clipNeedsResampling) if (clipNeedsResampling)
// The other clip's rate is different from ours, so resample // The other clip's rate is different from ours, so resample
if (!newClip->Resample(mRate)) if (!newClip->Resample(mRate))
@ -1542,7 +1569,11 @@ bool WaveClip::Paste(double t0, const WaveClip* other)
for (const auto &cutline: pastedClip->mCutLines) for (const auto &cutline: pastedClip->mCutLines)
{ {
mCutLines.push_back( mCutLines.push_back(
make_movable<WaveClip>(*cutline, mSequence->GetDirManager())); make_movable<WaveClip>
( *cutline, mSequence->GetDirManager(),
// Recursively copy cutlines of cutlines. They don't need
// their offsets adjusted.
true));
mCutLines.back()->Offset(t0 - mOffset); mCutLines.back()->Offset(t0 - mOffset);
} }
@ -1635,32 +1666,23 @@ bool WaveClip::ClearAndAddCutLine(double t0, double t1)
if (t0 > GetEndTime() || t1 < GetStartTime()) if (t0 > GetEndTime() || t1 < GetStartTime())
return true; // time out of bounds return true; // time out of bounds
auto newClip = make_movable<WaveClip> const double clip_t0 = std::max( t0, GetStartTime() );
(mSequence->GetDirManager(), mSequence->GetSampleFormat(), mRate); const double clip_t1 = std::min( t1, GetEndTime() );
double clip_t0 = t0;
double clip_t1 = t1; auto newClip = make_movable<WaveClip>
if (clip_t0 < GetStartTime()) (*this, mSequence->GetDirManager(), true, clip_t0, clip_t1);
clip_t0 = GetStartTime();
if (clip_t1 > GetEndTime())
clip_t1 = GetEndTime();
newClip->SetOffset(this->mOffset);
if (!newClip->CreateFromCopy(clip_t0, clip_t1, this))
return false;
newClip->SetOffset(clip_t0-mOffset); newClip->SetOffset(clip_t0-mOffset);
// Sort out cutlines that belong to the NEW cutline // Remove cutlines from this clip that were in the selection, shift
// left those that were after the selection
// May DELETE as we iterate, so don't use range-for // May DELETE as we iterate, so don't use range-for
for (auto it = mCutLines.begin(); it != mCutLines.end();) for (auto it = mCutLines.begin(); it != mCutLines.end();)
{ {
WaveClip* clip = it->get(); WaveClip* clip = it->get();
double cutlinePosition = mOffset + clip->GetOffset(); double cutlinePosition = mOffset + clip->GetOffset();
if (cutlinePosition >= t0 && cutlinePosition <= t1) if (cutlinePosition >= t0 && cutlinePosition <= t1)
{
clip->SetOffset(cutlinePosition - newClip->GetOffset() - mOffset);
newClip->mCutLines.push_back(std::move(*it)); // transfer ownership!!
it = mCutLines.erase(it); it = mCutLines.erase(it);
}
else else
{ {
if (cutlinePosition >= t1) if (cutlinePosition >= t1)
@ -1755,11 +1777,6 @@ bool WaveClip::RemoveCutLine(double cutLinePosition)
return false; return false;
} }
void WaveClip::RemoveAllCutLines()
{
mCutLines.clear();
}
void WaveClip::OffsetCutLines(double t0, double len) void WaveClip::OffsetCutLines(double t0, double len)
{ {
for (const auto &cutLine : mCutLines) for (const auto &cutLine : mCutLines)
@ -1799,6 +1816,9 @@ void WaveClip::SetRate(int rate)
bool WaveClip::Resample(int rate, ProgressDialog *progress) bool WaveClip::Resample(int rate, ProgressDialog *progress)
{ {
// Note: it is not necessary to do this recursively to cutlines.
// They get resampled as needed when they are expanded.
if (rate == mRate) if (rate == mRate)
return true; // Nothing to do return true; // Nothing to do

View File

@ -215,7 +215,15 @@ public:
// essentially a copy constructor - but you must pass in the // essentially a copy constructor - but you must pass in the
// current project's DirManager, because we might be copying // current project's DirManager, because we might be copying
// from one project to another // from one project to another
WaveClip(const WaveClip& orig, const std::shared_ptr<DirManager> &projDirManager); WaveClip(const WaveClip& orig,
const std::shared_ptr<DirManager> &projDirManager,
bool copyCutlines);
// Copy only a range from the given WaveClip
WaveClip(const WaveClip& orig,
const std::shared_ptr<DirManager> &projDirManager,
bool copyCutlines,
double t0, double t1);
virtual ~WaveClip(); virtual ~WaveClip();
@ -266,9 +274,6 @@ public:
* 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() { mDirty++; }
/// Create clip from copy, discarding previous information in the clip
bool CreateFromCopy(double t0, double t1, const WaveClip* other);
/** 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 */
bool GetWaveDisplay(WaveDisplay &display, bool GetWaveDisplay(WaveDisplay &display,
@ -339,7 +344,6 @@ public:
/// Remove cut line, without expanding the audio in it /// Remove cut line, without expanding the audio in it
bool RemoveCutLine(double cutLinePosition); bool RemoveCutLine(double cutLinePosition);
void RemoveAllCutLines();
/// Offset cutlines right to time 't0' by time amount 'len' /// Offset cutlines right to time 't0' by time amount 'len'
void OffsetCutLines(double t0, double len); void OffsetCutLines(double t0, double len);
@ -367,35 +371,35 @@ public:
XMLTagHandler *HandleXMLChild(const wxChar *tag) override; XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
void WriteXML(XMLWriter &xmlFile) const /* not override */; void WriteXML(XMLWriter &xmlFile) const /* not override */;
// Cache of values to colour pixels of Spectrogram - used by TrackArtist
mutable std::unique_ptr<SpecPxCache> mSpecPxCache;
// AWD, Oct 2009: for pasting whitespace at the end of selection // AWD, Oct 2009: for pasting whitespace at the end of selection
bool GetIsPlaceholder() const { return mIsPlaceholder; } bool GetIsPlaceholder() const { return mIsPlaceholder; }
void SetIsPlaceholder(bool val) { mIsPlaceholder = val; } void SetIsPlaceholder(bool val) { mIsPlaceholder = val; }
protected: public:
mutable wxRect mDisplayRect; // Cache of values to colour pixels of Spectrogram - used by TrackArtist
mutable std::unique_ptr<SpecPxCache> mSpecPxCache;
double mOffset; protected:
mutable wxRect mDisplayRect {};
double mOffset { 0 };
int mRate; int mRate;
int mDirty; int mDirty { 0 };
bool mIsCutLine;
std::unique_ptr<Sequence> mSequence; std::unique_ptr<Sequence> mSequence;
std::unique_ptr<Envelope> mEnvelope; std::unique_ptr<Envelope> mEnvelope;
mutable std::unique_ptr<WaveCache> mWaveCache; mutable std::unique_ptr<WaveCache> mWaveCache;
mutable ODLock mWaveCacheMutex; mutable ODLock mWaveCacheMutex {};
mutable std::unique_ptr<SpecCache> mSpecCache; mutable std::unique_ptr<SpecCache> mSpecCache;
SampleBuffer mAppendBuffer; SampleBuffer mAppendBuffer {};
size_t mAppendBufferLen; size_t mAppendBufferLen { 0 };
// Cut Lines are nothing more than ordinary wave clips, with the // Cut Lines are nothing more than ordinary wave clips, with the
// offset relative to the start of the clip. // offset relative to the start of the clip.
WaveClipHolders mCutLines; WaveClipHolders mCutLines {};
// AWD, Oct. 2009: for whitespace-at-end-of-selection pasting // AWD, Oct. 2009: for whitespace-at-end-of-selection pasting
bool mIsPlaceholder; bool mIsPlaceholder { false };
}; };
#endif #endif

View File

@ -132,7 +132,8 @@ WaveTrack::WaveTrack(const WaveTrack &orig):
Init(orig); Init(orig);
for (const auto &clip : orig.mClips) for (const auto &clip : orig.mClips)
mClips.push_back(make_movable<WaveClip>(*clip, mDirManager)); mClips.push_back
( make_movable<WaveClip>( *clip, mDirManager, true ) );
} }
// Copy the track metadata but not the contents. // Copy the track metadata but not the contents.
@ -630,7 +631,7 @@ bool WaveTrack::Trim (double t0, double t1)
Track::Holder WaveTrack::Copy(double t0, double t1) const Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const
{ {
if (t1 <= t0) if (t1 <= t0)
return{}; return{};
@ -641,6 +642,10 @@ Track::Holder WaveTrack::Copy(double t0, double t1) const
newTrack->Init(*this); newTrack->Init(*this);
// PRL: Why shouldn't cutlines be copied and pasted too? I don't know, but
// that was the old behavior. But this function is also used by the
// Duplicate command and I changed its behavior in that case.
for (const auto &clip : mClips) for (const auto &clip : mClips)
{ {
if (t0 <= clip->GetStartTime() && t1 >= clip->GetEndTime()) if (t0 <= clip->GetStartTime() && t1 >= clip->GetEndTime())
@ -649,25 +654,20 @@ Track::Holder WaveTrack::Copy(double t0, double t1) const
//printf("copy: clip %i is in copy region\n", (int)clip); //printf("copy: clip %i is in copy region\n", (int)clip);
newTrack->mClips.push_back newTrack->mClips.push_back
(make_movable<WaveClip>(*clip, mDirManager)); (make_movable<WaveClip>(*clip, mDirManager, ! forClipboard));
WaveClip *const newClip = newTrack->mClips.back().get(); WaveClip *const newClip = newTrack->mClips.back().get();
newClip->RemoveAllCutLines();
newClip->Offset(-t0); newClip->Offset(-t0);
} }
else else if (t1 > clip->GetStartTime() && t0 < clip->GetEndTime())
if (t1 > clip->GetStartTime() && t0 < clip->GetEndTime())
{ {
// Clip is affected by command // Clip is affected by command
//printf("copy: clip %i is affected by command\n", (int)clip); //printf("copy: clip %i is affected by command\n", (int)clip);
auto newClip = make_movable<WaveClip>(*clip, mDirManager); const double clip_t0 = std::max(t0, clip->GetStartTime());
newClip->RemoveAllCutLines(); const double clip_t1 = std::min(t1, clip->GetEndTime());
double clip_t0 = t0;
double clip_t1 = t1; auto newClip = make_movable<WaveClip>
if (clip_t0 < clip->GetStartTime()) (*clip, mDirManager, ! forClipboard, clip_t0, clip_t1);
clip_t0 = clip->GetStartTime();
if (clip_t1 > clip->GetEndTime())
clip_t1 = clip->GetEndTime();
//printf("copy: clip_t0=%f, clip_t1=%f\n", clip_t0, clip_t1); //printf("copy: clip_t0=%f, clip_t1=%f\n", clip_t0, clip_t1);
@ -675,27 +675,16 @@ Track::Holder WaveTrack::Copy(double t0, double t1) const
if (newClip->GetOffset() < 0) if (newClip->GetOffset() < 0)
newClip->SetOffset(0); newClip->SetOffset(0);
//printf("copy: clip offset is now %f\n", newClip->GetOffset()); newTrack->mClips.push_back(std::move(newClip)); // transfer ownership
if (!newClip->CreateFromCopy(clip_t0, clip_t1, clip.get()))
{
//printf("paste: CreateFromCopy(%f, %f, %i) returns false, quitting\n",
// clip_t0, clip_t1, (int)clip);
// JKC: July 2007, previously we did 'return false' here which
// could leave *dest undefined.
// I think this is dealing with clips that don't have any sequence content
// i.e. we don't copy cut lines and such - anyone like to explain more?
}
else
{
newTrack->mClips.push_back(std::move(newClip)); // transfer ownership
}
} }
} }
// AWD, Oct 2009: If the selection ends in whitespace, create a placeholder // AWD, Oct 2009: If the selection ends in whitespace, create a placeholder
// clip representing that whitespace // clip representing that whitespace
if (newTrack->GetEndTime() + 1.0 / newTrack->GetRate() < t1 - t0) // PRL: Only if we want the track for pasting into other tracks. Not if it
// goes directly into a project as in the Duplicate command.
if (forClipboard &&
newTrack->GetEndTime() + 1.0 / newTrack->GetRate() < t1 - t0)
{ {
auto placeholder = make_movable<WaveClip>(mDirManager, auto placeholder = make_movable<WaveClip>(mDirManager,
newTrack->GetSampleFormat(), newTrack->GetSampleFormat(),
@ -1069,7 +1058,8 @@ bool WaveTrack::HandleClear(double t0, double t1,
{ {
if (!clip->ClearAndAddCutLine(t0,t1)) if (!clip->ClearAndAddCutLine(t0,t1))
return false; return false;
} else }
else
{ {
if (split) { if (split) {
// Three cases: // Three cases:
@ -1088,11 +1078,13 @@ bool WaveTrack::HandleClear(double t0, double t1,
// NEW clips out of the left and right halves... // NEW clips out of the left and right halves...
// left // left
clipsToAdd.push_back(make_movable<WaveClip>(*clip, mDirManager)); clipsToAdd.push_back
( make_movable<WaveClip>( *clip, mDirManager, true ) );
clipsToAdd.back()->Clear(t0, clip->GetEndTime()); clipsToAdd.back()->Clear(t0, clip->GetEndTime());
// right // right
clipsToAdd.push_back(make_movable<WaveClip>(*clip, mDirManager)); clipsToAdd.push_back
( make_movable<WaveClip>( *clip, mDirManager, true ) );
WaveClip *const right = clipsToAdd.back().get(); WaveClip *const right = clipsToAdd.back().get();
right->Clear(clip->GetStartTime(), t1); right->Clear(clip->GetStartTime(), t1);
right->Offset(t1 - clip->GetStartTime()); right->Offset(t1 - clip->GetStartTime());
@ -1349,7 +1341,8 @@ bool WaveTrack::Paste(double t0, const Track *src)
// AWD Oct. 2009: Don't actually paste in placeholder clips // AWD Oct. 2009: Don't actually paste in placeholder clips
if (!clip->GetIsPlaceholder()) if (!clip->GetIsPlaceholder())
{ {
auto newClip = make_movable<WaveClip>(*clip, mDirManager); auto newClip =
make_movable<WaveClip>( *clip, mDirManager, true );
newClip->Resample(mRate); newClip->Resample(mRate);
newClip->Offset(t0); newClip->Offset(t0);
newClip->MarkChanged(); newClip->MarkChanged();
@ -2377,7 +2370,7 @@ bool WaveTrack::SplitAt(double t)
if(t - 1.0/c->GetRate() >= c->GetOffset()) if(t - 1.0/c->GetRate() >= c->GetOffset())
c->GetEnvelope()->Insert(t - c->GetOffset() - 1.0/c->GetRate(), val); // frame end points c->GetEnvelope()->Insert(t - c->GetOffset() - 1.0/c->GetRate(), val); // frame end points
c->GetEnvelope()->Insert(t - c->GetOffset(), val); c->GetEnvelope()->Insert(t - c->GetOffset(), val);
auto newClip = make_movable<WaveClip>(*c, mDirManager); auto newClip = make_movable<WaveClip>( *c, mDirManager, true );
if (!c->Clear(t, c->GetEndTime())) if (!c->Clear(t, c->GetEndTime()))
{ {
return false; return false;

View File

@ -167,8 +167,14 @@ class AUDACITY_DLL_API WaveTrack final : public Track {
// //
Track::Holder Cut(double t0, double t1) override; Track::Holder Cut(double t0, double t1) override;
Track::Holder Copy(double t0, double t1) const override;
// If forClipboard is true,
// and there is no clip at the end time of the selection, then the result
// will contain a "placeholder" clip whose only purpose is to make
// GetEndTime() correct. This clip is not re-copied when pasting.
Track::Holder Copy(double t0, double t1, bool forClipboard = true) const override;
Track::Holder CopyNonconst(double t0, double t1) /* not override */; Track::Holder CopyNonconst(double t0, double t1) /* not override */;
bool Clear(double t0, double t1) override; bool Clear(double t0, double t1) override;
bool Paste(double t0, const Track *src) override; bool Paste(double t0, const Track *src) override;
bool ClearAndPaste(double t0, double t1, bool ClearAndPaste(double t0, double t1,