Skip to content

Commit

Permalink
Rewrite WaveTrack pasting as interval-major
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul-Licameli committed Nov 11, 2023
1 parent 6097cf4 commit 4aa9658
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 65 deletions.
142 changes: 79 additions & 63 deletions libraries/lib-wave-track/WaveTrack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ void WaveTrack::Interval::ClearRight(double t)
GetClip(channel)->ClearRight(t);
}

void WaveTrack::Interval::ShiftBy(double delta)
{
ForEachClip([=](auto &clip){ clip.ShiftBy(delta); });
}

void WaveTrack::Interval::StretchLeftTo(double t)
{
for(unsigned channel = 0; channel < NChannels(); ++channel)
Expand Down Expand Up @@ -363,6 +368,11 @@ bool WaveTrack::Interval::StretchRatioEquals(double value) const
return true;
}

bool WaveTrack::Interval::HasEqualStretchRatio(const Interval& other) const
{
return StretchRatioEquals(other.GetStretchRatio());
}

/** Insert silence at the end, and causes the envelope to ramp
linearly to the given value */
void WaveTrack::Interval::AppendSilence(double len, double envelopeValue)
Expand Down Expand Up @@ -402,6 +412,11 @@ int WaveTrack::Interval::GetColorIndex() const
return mpClip->GetColourIndex();
}

sampleCount WaveTrack::Interval::GetPlayStartSample() const
{
return mpClip->GetPlayStartSample();
}

void WaveTrack::Interval::SetPlayStartTime(double time)
{
ForEachClip([&](auto& clip) { clip.SetPlayStartTime(time); });
Expand Down Expand Up @@ -480,6 +495,17 @@ bool WaveTrack::Interval::IsPlaceholder() const
return mpClip->GetIsPlaceholder();
}

void WaveTrack::Interval::MarkChanged()
{
ForEachClip([](auto &clip){ clip.MarkChanged(); });
}

void WaveTrack::Interval::Resample(int rate, BasicUI::ProgressDialog *progress)
{
// TODO: correct the denominator of the progress, when it's not null
ForEachClip([=](auto &clip){ clip.Resample(rate, progress); });
}

const Envelope& WaveTrack::Interval::GetEnvelope() const
{
return *mpClip->GetEnvelope();
Expand Down Expand Up @@ -952,28 +978,19 @@ Track::Holder WaveTrack::PasteInto(
assert(IsLeader());
auto &trackFactory = WaveTrackFactory::Get(project);
auto &pSampleBlockFactory = trackFactory.GetSampleBlockFactory();
Track::Holder pFirstTrack;
const WaveTrack *pFirstChannel{};
for (const auto pChannel : TrackList::Channels(this)) {
auto pNewTrack = pChannel->EmptyCopy(pSampleBlockFactory);
list.Add(pNewTrack);
assert(pNewTrack->IsLeader() == pChannel->IsLeader());
if (!pFirstTrack) {
pFirstTrack = pNewTrack;
pFirstChannel = pChannel;
}
}
pFirstTrack->Paste(0.0, *pFirstChannel);
return pFirstTrack;
auto tmpList = WideEmptyCopy(pSampleBlockFactory);
auto pFirstTrack = *tmpList->Any<WaveTrack>().begin();
list.Append(std::move(*tmpList));
pFirstTrack->Paste(0.0, *this);
return pFirstTrack->SharedPointer();
}

size_t WaveTrack::NIntervals() const
{
return mClips.size();
}

std::shared_ptr<WideChannelGroupInterval>
WaveTrack::DoGetInterval(size_t iInterval)
auto WaveTrack::GetWideClip(size_t iInterval) -> IntervalHolder
{
if (iInterval < NIntervals()) {
WaveClipHolder pClip = mClips[iInterval],
Expand All @@ -989,6 +1006,17 @@ WaveTrack::DoGetInterval(size_t iInterval)
return {};
}

auto WaveTrack::GetWideClip(size_t iInterval) const -> IntervalConstHolder
{
return const_cast<WaveTrack&>(*this).GetWideClip(iInterval);
}

std::shared_ptr<WideChannelGroupInterval>
WaveTrack::DoGetInterval(size_t iInterval)
{
return GetWideClip(iInterval);
}

const WaveClip* WaveTrack::FindClipByName(const wxString& name) const
{
for (const auto& clip : mClips)
Expand Down Expand Up @@ -2147,18 +2175,9 @@ void WaveTrack::PasteWaveTrackAtSameTempo(
GetProjectTempo() == other.GetProjectTempo());
const auto startTime = other.GetStartTime();
const auto endTime = other.GetEndTime();
auto iter = TrackList::Channels(&other).begin();
for (const auto pChannel : TrackList::Channels(this)) {
PasteOne(*pChannel, t0, **iter, startTime, endTime, merge);
if (otherNChannels > 1)
++iter;
}
}

void WaveTrack::PasteOne(
WaveTrack& track, double t0, const WaveTrack& other, const double startTime,
const double insertDuration, bool merge)
{
const auto insertDuration = endTime;
auto &track = *this;
//
// Pasting is a bit complicated, because with the existence of multiclip mode,
// we must guess the behaviour the user wants.
Expand Down Expand Up @@ -2187,7 +2206,7 @@ void WaveTrack::PasteOne(

//wxPrintf("paste: we have at least one clip\n");

const auto clipAtT0 = track.GetClipAtTime(t0);
const auto clipAtT0 = track.GetIntervalAtTime(t0);
const auto otherFirstClip = other.GetLeftmostClip();
const auto otherLastClip = other.GetRightmostClip();
const auto stretchRatiosMatch =
Expand Down Expand Up @@ -2222,21 +2241,21 @@ void WaveTrack::PasteOne(

// Make room for the pasted data
if (editClipCanMove) {
if (!singleClipMode) {
if (!singleClipMode)
// We need to insert multiple clips, so split the current clip and ...
track.SplitAt(t0);
}
track.Split(t0, t0);

//else if there is a clip at t0 insert new clip inside it and ...

// ... move everything to the right
for (const auto& clip : track.mClips)
for (const auto& clip : track.Intervals())
if (clip->GetPlayStartTime() > t0 - (1.0 / rate))
clip->ShiftBy(insertDuration);
}
else
{
if (!merge)
track.SplitAt(t0);
track.Split(t0, t0);
const auto clipAtT0 = track.GetClipAtTime(t0);
const auto t = clipAtT0 ? clipAtT0->GetPlayEndTime() : t0;
if (!track.IsEmpty(t, t + insertDuration))
Expand All @@ -2249,13 +2268,13 @@ void WaveTrack::PasteOne(
// Single clip mode
// wxPrintf("paste: checking for single clip mode!\n");

WaveClip* insideClip = nullptr;
for (const auto& clip : track.mClips) {
IntervalHolder insideClip{};
for (const auto& clip : track.Intervals()) {
if (editClipCanMove) {
if (clip->WithinPlayRegion(t0)) {
//wxPrintf("t0=%.6f: inside clip is %.6f ... %.6f\n",
// t0, clip->GetStartTime(), clip->GetEndTime());
insideClip = clip.get();
insideClip = clip;
break;
}
}
Expand All @@ -2264,7 +2283,7 @@ void WaveTrack::PasteOne(
if (clip->WithinPlayRegion(t0) ||
track.TimeToLongSamples(t0) == clip->GetPlayStartSample())
{
insideClip = clip.get();
insideClip = clip;
break;
}
}
Expand All @@ -2285,7 +2304,7 @@ void WaveTrack::PasteOne(
throw notEnoughSpaceException;
}
}
if (auto *pClip = other.GetClipByIndex(0)) {
if (auto pClip = other.GetWideClip(0)) {
// This branch only gets executed in `singleClipMode` - we've
// already made sure that stretch ratios are equal, satisfying
// `WaveClip::Paste`'s precondition.
Expand All @@ -2310,21 +2329,18 @@ void WaveTrack::PasteOne(
// not that it matters.
throw notEnoughSpaceException;

for (const auto& clip : other.mClips) {
for (const auto& clip : other.Intervals()) {
// AWD Oct. 2009: Don't actually paste in placeholder clips
if (!clip->GetIsPlaceholder()) {
auto newClip =
std::make_shared<WaveClip>(*clip, track.mpFactory, true);
newClip->Resample(rate);
newClip->ShiftBy(t0);
newClip->MarkChanged();
if (pastingFromTempTrack)
if (!clip->IsPlaceholder()) {
const auto name = (pastingFromTempTrack)
//Clips from the tracks which aren't bound to any TrackList are
//considered to be new entities, thus named using "new" name template
newClip->SetName(track.MakeNewClipName());
else
newClip->SetName(track.MakeClipCopyName(clip->GetName()));
track.InsertClip(std::move(newClip)); // transfer ownership
? track.MakeNewClipName()
: track.MakeClipCopyName(clip->GetName());
const auto newClip = CreateWideClip(t0, name);
newClip->Resample(rate);
newClip->MarkChanged();
track.InsertInterval(newClip); // transfer ownership
}
}
}
Expand Down Expand Up @@ -3011,26 +3027,26 @@ bool WaveTrack::CloseLock() noexcept
return true;
}

const WaveClip* WaveTrack::GetLeftmostClip() const {
auto WaveTrack::GetLeftmostClip() const -> IntervalConstHolder {
if (mClips.empty())
return nullptr;
return std::min_element(
mClips.begin(), mClips.end(),
[](const auto& a, const auto b) {
return a->GetPlayStartTime() < b->GetPlayStartTime();
})
->get();
const auto begin = mClips.begin(),
iter = std::min_element(begin, mClips.end(),
[](const auto& a, const auto b) {
return a->GetPlayStartTime() < b->GetPlayStartTime();
});
return GetWideClip(iter - begin);
}

const WaveClip* WaveTrack::GetRightmostClip() const {
auto WaveTrack::GetRightmostClip() const -> IntervalConstHolder {
if (mClips.empty())
return nullptr;
return std::max_element(
mClips.begin(), mClips.end(),
[](const auto& a, const auto b) {
return a->GetPlayEndTime() < b->GetPlayEndTime();
})
->get();
const auto begin = mClips.begin(),
iter = std::max_element(begin, mClips.end(),
[](const auto& a, const auto b) {
return a->GetPlayEndTime() < b->GetPlayEndTime();
});
return GetWideClip(iter - begin);
}

ClipConstHolders WaveTrack::GetClipInterfaces() const
Expand Down
25 changes: 23 additions & 2 deletions libraries/lib-wave-track/WaveTrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -719,8 +719,8 @@ class WAVE_TRACK_API WaveTrack final
const WaveClipConstHolders &GetClips() const
{ return reinterpret_cast< const WaveClipConstHolders& >( mClips ); }

const WaveClip* GetLeftmostClip() const;
const WaveClip* GetRightmostClip() const;
IntervalConstHolder GetLeftmostClip() const;
IntervalConstHolder GetRightmostClip() const;

/**
* @brief Get access to the (visible) clips in the tracks, in unspecified
Expand Down Expand Up @@ -972,6 +972,10 @@ class WAVE_TRACK_API WaveTrack final
void SetColorIndex(int index);
int GetColorIndex() const;

//! Real start time of the clip, quantized to raw sample rate (track's
//! rate)
sampleCount GetPlayStartSample() const;

void SetPlayStartTime(double time);
double GetPlayStartTime() const;
double GetPlayEndTime() const;
Expand Down Expand Up @@ -1010,14 +1014,21 @@ class WAVE_TRACK_API WaveTrack final
void ClearLeft(double t);
void ClearRight(double t);

void ShiftBy(double delta);

/*!
* @post result: `result->GetStretchRatio() == 1`
*/
std::shared_ptr<Interval> GetStretchRenderedCopy(
const std::function<void(double)>& reportProgress,
const ChannelGroup& group, const SampleBlockFactoryPtr& factory,
sampleFormat format);

//! Checks for stretch-ratio equality, accounting for rounding errors.
//! @{
bool HasEqualStretchRatio(const Interval& other) const;
bool StretchRatioEquals(double value) const;
//! @}

std::shared_ptr<const WaveClip> GetClip(size_t iChannel) const
{ return iChannel == 0 ? mpClip : mpClip1; }
Expand All @@ -1030,6 +1041,14 @@ class WAVE_TRACK_API WaveTrack final

bool Paste(double t0, const Interval &src);

/** 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. */
/*! @excsafety{No-fail} */
void MarkChanged();

void Resample(int rate, BasicUI::ProgressDialog *progress = nullptr);

const Envelope& GetEnvelope() const;

private:
Expand Down Expand Up @@ -1140,6 +1159,8 @@ class WAVE_TRACK_API WaveTrack final
void
ReplaceInterval(const IntervalHolder& oldOne, const IntervalHolder& newOne);

IntervalHolder GetWideClip(size_t iClip);
IntervalConstHolder GetWideClip(size_t iClip) const;
std::shared_ptr<WideChannelGroupInterval> DoGetInterval(size_t iInterval)
override;
std::shared_ptr<::Channel> DoGetChannel(size_t iChannel) override;
Expand Down

0 comments on commit 4aa9658

Please sign in to comment.