43#include <unordered_set>
80 const std::function<
void(
double)>& reportProgress,
83 auto &interval = *pInterval;
85 if (!interval.HasPitchOrSpeed())
88 const auto dst = std::make_shared<Interval>(
91 const auto originalPlayStartTime = interval.GetPlayStartTime();
92 const auto originalPlayEndTime = interval.GetPlayEndTime();
93 const auto stretchRatio = interval.GetStretchRatio();
99 interval.TrimLeftTo(originalPlayStartTime);
100 interval.TrimRightTo(originalPlayEndTime);
107 const auto tmpPlayStartTime =
108 std::max(interval.GetSequenceStartTime(), originalPlayStartTime - stretchRatio);
109 const auto tmpPlayEndTime =
110 std::min(interval.GetSequenceEndTime(), originalPlayEndTime + stretchRatio);
111 interval.TrimLeftTo(tmpPlayStartTime);
112 interval.TrimRightTo(tmpPlayEndTime);
114 constexpr auto sourceDurationToDiscard = 0.;
115 constexpr auto blockSize = 1024;
116 const auto numChannels = interval.NChannels();
120 params.timeRatio = stretchRatio;
121 params.pitchRatio = std::pow(2., interval.GetCentShift() / 1200.);
125 stretcherSource, std::move(
params) };
128 const auto totalNumOutSamples =
135 while (numOutSamples < totalNumOutSamples)
137 const auto numSamplesToGet =
139 stretcher.GetSamples(container.
Get(), numSamplesToGet);
142 if (interval.NChannels() == 2)
145 numOutSamples += numSamplesToGet;
148 numOutSamples.as_double() / totalNumOutSamples.as_double());
154 dst->SetPlayStartTime(tmpPlayStartTime);
155 dst->ClearLeft(originalPlayStartTime);
156 dst->ClearRight(originalPlayEndTime);
159 auto dstEnvelope = std::make_unique<Envelope>(interval.GetEnvelope());
160 const auto samplePeriod = 1. / interval.GetRate();
161 dstEnvelope->CollapseRegion(
162 originalPlayEndTime, interval.GetSequenceEndTime() + samplePeriod, samplePeriod);
163 dstEnvelope->CollapseRegion(0, originalPlayStartTime, samplePeriod);
164 dstEnvelope->SetOffset(originalPlayStartTime);
165 dst->SetEnvelope(move(dstEnvelope));
169 assert(!dst->HasPitchOrSpeed());
174std::shared_ptr<const WaveTrack::Interval>
177 std::shared_ptr<const Interval> result;
179 ? std::numeric_limits<double>::max()
180 : std::numeric_limits<double>::lowest();
184 (other->Start() > interval.
Start() && other->Start() < bestMatchTime))
187 (other->Start() < interval.
Start() && other->Start() > bestMatchTime)))
190 bestMatchTime = other->Start();
199 return std::const_pointer_cast<Interval>(
200 std::as_const(*this).GetNextInterval(interval, searchDirection));
207 if (interval->WithinPlayRegion(t))
223 double GetOrigin()
const;
224 void SetOrigin(
double origin);
229 float GetGain()
const;
230 void SetGain(
float value);
231 float GetPan()
const;
232 void SetPan(
float value);
235 void SetRate(
int value);
239 std::atomic<float> mGain{ 1.0f };
241 std::atomic<float> mPan{ 0.0f };
244 double mOrigin{ 0.0 };
250 [](
auto &) {
return std::make_unique<WaveTrackData>(); } };
253WaveTrackData::WaveTrackData(
const WaveTrackData &other) {
254 SetGain(other.GetGain());
255 SetPan(other.GetPan());
257 mOrigin = other.mOrigin;
258 mFormat = other.mFormat;
261WaveTrackData::~WaveTrackData() =
default;
264 return std::make_unique<WaveTrackData>(*
this);
276double WaveTrackData::GetOrigin()
const
280void WaveTrackData::SetOrigin(
double origin)
295float WaveTrackData::GetGain()
const
297 return mGain.load(std::memory_order_relaxed);
300void WaveTrackData::SetGain(
float value)
302 mGain.store(value, std::memory_order_relaxed);
305float WaveTrackData::GetPan()
const
307 return mPan.load(std::memory_order_relaxed);
310void WaveTrackData::SetPan(
float value)
312 mPan.store(value, std::memory_order_relaxed);
320void WaveTrackData::SetRate(
int value)
329 if (a.size() != b.size())
332 const auto compare = [](
const auto &a,
const auto &b) {
336 return a->GetPlayStartTime() == b->GetPlayStartTime() &&
337 a->GetSequenceStartTime() == b->GetSequenceStartTime() &&
338 a->GetPlayEndTime() == b->GetPlayEndTime() &&
339 a->GetSequenceEndTime() == b->GetSequenceEndTime();
342 return std::mismatch(a.begin(), a.end(), b.begin(), compare).first == a.end();
401 auto result = std::make_shared<WaveTrack>(
406 result->CreateRight();
409 result->AttachedTrackObjects::BuildAll();
420 assert(nChannels > 0);
421 assert(nChannels <= 2);
436 assert(pLeft && pRight);
437 const auto pLeftAttachments =
439 const auto pRightAttachments =
442 assert((pLeftAttachments ==
nullptr) == (pRightAttachments ==
nullptr));
443 if (pLeftAttachments) {
445 pRightAttachments->Reparent(shared_from_this());
447 pLeftAttachments->MakeStereo(shared_from_this(),
448 std::move(*pRightAttachments));
458 if (
const auto pAttachments =
460 pAttachments->Erase(shared_from_this(), ii);
466 assert(nChannels > 0);
467 assert(nChannels <= 2);
492 auto result =
tracks.Add(trackFactory.Create());
498 : mpFactory(pFactory)
514 result->AttachedTrackObjects::BuildAll();
521 for (
const auto &clip : orig)
523 std::make_shared<WaveClip>(*clip, pFactory,
true),
524 false, backup,
false);
569 for (
const auto &pInterval :
Intervals())
571 pInterval->ShiftBy(delta);
579 return std::static_pointer_cast<WaveTrack>(srcCopy);
589 for (
auto it = clips.begin(); it != clips.end();)
591 if ((*it)->IsEmpty())
592 it = clips.erase(it);
603 if (next ==
nullptr) {
606 wxLogWarning(L
"Right track %s is expected to be a WaveTrack."
607 "\n Removing link from left wave track %s.",
628 removeZeroClips(next->NarrowClips());
643 next->mLegacyRate = 0;
662 {
"wave",
"wave",
XO(
"Wave Track") },
681 auto &pSampleBlockFactory = trackFactory.GetSampleBlockFactory();
682 auto pFirstTrack =
EmptyCopy(pSampleBlockFactory);
683 list.
Add(pFirstTrack->SharedPointer());
684 pFirstTrack->Paste(0.0, *
this);
685 return pFirstTrack->SharedPointer();
695 return std::static_pointer_cast<Interval>(DoGetInterval(iInterval));
703std::shared_ptr<WideChannelGroupInterval>
714 return std::any_of(clips.begin(), clips.end(),
715 [&](
const auto &pClip){ return pClip->GetName() == name; });
728 return { shared_from_this(), &aliased };
736std::shared_ptr<WaveClipChannel>
738 ::Channel::GetInterval<WaveClipChannel>(iInterval); }
740std::shared_ptr<const WaveClipChannel>
742 ::Channel::GetInterval<const WaveClipChannel>(iInterval); }
749 return ::Channel::Intervals<const WaveClipChannel>(); }
764 newTrack->CopyClips(newTrack->mClips,
765 newTrack->mpFactory, this->mClips, backup);
771 auto name = originalName;
772 for (
auto i = 1;; ++i)
777 name =
XC(
"%s.%i",
"clip name template").Format(originalName, i).Translation();
784 for (
auto i = 1;; ++i)
789 name =
XC(
"%s %i",
"clip name template").Format(
GetName(), i).Translation();
806 newRate = std::max( 1.0, newRate );
810 clip->SetRate(newRate);
816 data.SetRate(
static_cast<int>(newRate));
851 else if (newPan < -1.0)
854 if (
GetPan() != newPan ) {
872 const auto pan =
GetPan();
879 if ((channel % 2) == 0)
890 result += clip->GetVisibleSampleCount();
902 const std::function<
void(
size_t)> & progressReport)
905 pClip->ConvertToSampleFormat(
format, progressReport);
918 if (clip->IntersectsPlayRegion(t0, t1)) {
937 auto result =
Copy(t0, t1);
949 auto result = Copy(t0, t1);
951 return std::static_pointer_cast<WaveTrack>(result);
959 bool inside0 =
false;
960 bool inside1 =
false;
963 if (t1 > clip->GetPlayStartTime() && t1 < clip->GetPlayEndTime()) {
965 clip->GetTrimRight() + clip->GetPlayEndTime() - t1);
969 if (t0 > clip->GetPlayStartTime() && t0 < clip->GetPlayEndTime()) {
971 clip->GetTrimLeft() + t0 - clip->GetPlayStartTime());
979 ; !inside1 && t1 < endTime
984 ; !inside0 && t0 > startTime
993 auto result = std::make_shared<WaveTrack>(
CreateToken{},
996 result->CreateRight();
1004 result->DoSetRate(rate);
1005 result->mpFactory = pFactory ? pFactory :
mpFactory;
1013 return EmptyCopy(NChannels(), pFactory);
1019 for (
auto &pClip :
mClips)
1020 pClip->DiscardRightChannel();
1026 assert(!GetOwner());
1034 result->Add(newTrack);
1038 return std::static_pointer_cast<WaveTrack>(result->DetachFirst());
1043 std::vector<Holder> result{ SharedPointer<WaveTrack>() };
1044 if (NChannels() == 2) {
1045 auto pOwner = GetOwner();
1047 auto pNewTrack = result.emplace_back(EmptyCopy(1));
1048 for (
auto &pClip : mClips)
1049 pNewTrack->mClips.emplace_back(pClip->SplitChannels());
1050 this->mRightChannel.reset();
1051 auto iter = pOwner->Find(
this);
1052 pOwner->Insert(*++iter, pNewTrack);
1054 result[0]->EraseChannelAttachments(1);
1055 result[1]->EraseChannelAttachments(0);
1063 for (
const auto &pClip:
mClips)
1064 pClip->SwapChannels();
1066 if (
const auto pAttachments =
1068 pAttachments->SwapChannels(shared_from_this());
1079 const auto endTime = std::max(
GetEndTime(), t1);
1084 if (pClip->IsEmpty())
1086 else if (t0 <= pClip->GetPlayStartTime() && t1 >= pClip->GetPlayEndTime())
1088 newTrack->CopyWholeClip(*pClip, t0, forClipboard);
1090 else if (pClip->CountSamples(t0, t1) >= 1) {
1091 newTrack->CopyPartOfClip(*pClip, t0, t1, forClipboard);
1094 newTrack->FinishCopy(t0, t1, endTime, forClipboard);
1099 double t0,
bool forClipboard)
1102 const auto newClip =
1103 std::make_shared<Interval>(clip, pFactory, !forClipboard);
1105 newClip->ShiftBy(-t0);
1109 double t0,
double t1,
bool forClipboard)
1112 auto newClip = std::make_shared<Interval>(
1113 clip, pFactory, !forClipboard, t0, t1);
1114 newClip->SetName(clip.GetName());
1115 newClip->ShiftBy(-t0);
1116 if (newClip->GetPlayStartTime() < 0)
1117 newClip->SetPlayStartTime(0);
1122 double t0,
double t1,
double endTime,
bool forClipboard)
1128 if (forClipboard && endTime + 1.0 /
GetRate() < t1 - t0) {
1130 placeholder->SetIsPlaceholder(
true);
1131 placeholder->InsertSilence(0, (t1 - t0) -
GetEndTime());
1203 bool clearByTrimming)
1208 if (!tempo.has_value())
1212 t0, t1, *copyHolder, preserve, merge, effectWarper, clearByTrimming);
1216 double t0,
double t1,
const WaveTrack& src,
bool preserve,
bool merge,
1217 const TimeWarper* effectWarper,
bool clearByTrimming)
1219 const auto srcNChannels = src.
NChannels();
1229 double dur =
std::min(t1 - t0, endTime);
1238 auto &track = *
this;
1240 std::vector<SplitInfo> splits;
1245 auto get_split = [&](
double time) {
1246 auto it = std::find_if(splits.begin(), splits.end(),
1247 [time](
const SplitInfo& split) { return split.time == time; });
1248 if(it == splits.end())
1251 { time, nullptr, nullptr, std::nullopt, std::nullopt }
1258 const TimeWarper *warper = (effectWarper ? effectWarper : &localWarper);
1260 const auto roundTime = [&track](
double t){
1261 return track.SnapToSample(t);
1271 for (
const auto &&clip : track.Intervals()) {
1277 st = roundTime(clip->GetPlayStartTime());
1278 if (st >= t0 && st <= t1) {
1279 auto it = get_split(st);
1280 if (clip->GetTrimLeft() != 0) {
1282 it->right = track.CopyClip(*clip,
false);
1283 it->right->SetTrimLeft(.0);
1284 it->right->ClearRight(clip->GetPlayStartTime());
1286 it->rightClipName = clip->GetName();
1289 st = roundTime(clip->GetPlayEndTime());
1290 if (st >= t0 && st <= t1) {
1291 auto it = get_split(st);
1292 if (clip->GetTrimRight() != 0) {
1294 it->left = track.CopyClip(*clip,
false);
1295 it->left->SetTrimRight(.0);
1296 it->left->ClearLeft(clip->GetPlayEndTime());
1298 it->leftClipName = clip->GetName();
1302 auto cutlines = clip->GetCutLines();
1303 for (
auto &cut : cutlines) {
1304 const auto unrounded =
1305 clip->GetSequenceStartTime() + cut->GetSequenceStartTime();
1306 const double cs = roundTime(unrounded);
1309 if (cs >= t0 && cs <= t1) {
1311 cut->SetSequenceStartTime(cs);
1312 bool removed = clip->RemoveCutLine(unrounded);
1314 cuts.push_back(move(cut));
1319 const auto tolerance = 2.0 / track.GetRate();
1322 constexpr auto split =
false;
1325 track.HandleClear(t0, t1,
false, split, clearByTrimming);
1328 track.PasteWaveTrackAtSameTempo(t0, src, merge);
1331 if (merge && splits.size() > 0) {
1337 auto clips = track.SortedIntervalArray();
1342 for (
const auto clip : clips) {
1345 if (fabs(t1 - clip->GetPlayStartTime()) < tolerance) {
1346 if (prev && clip->HasEqualPitchAndSpeed(*prev))
1348 track.GetClipIndex(*prev), track.GetClipIndex(*clip));
1357 auto clips = track.SortedIntervalArray();
1362 for (
const auto clip : clips) {
1367 if (clip->HasEqualPitchAndSpeed(*prev))
1369 track.GetClipIndex(*prev), track.GetClipIndex(*clip));
1372 if (fabs(t0 - clip->GetPlayEndTime()) < tolerance)
1388 assert(target.GetTrimLeft() == 0);
1389 if (target.GetTrimLeft() != 0)
1395 assert(target.HasEqualPitchAndSpeed(src));
1397 auto trim = src.GetPlayEndTime() - src.GetPlayStartTime();
1398 auto success = target.Paste(target.GetPlayStartTime(), src);
1400 target.SetTrimLeft(trim);
1403 target.ShiftBy(-trim);
1409 assert(target.GetTrimRight() == 0);
1410 if (target.GetTrimRight() != 0)
1413 assert(target.HasEqualPitchAndSpeed(src));
1415 auto trim = src.GetPlayEndTime() - src.GetPlayStartTime();
1416 auto success = target.Paste(target.GetPlayEndTime(), src);
1418 target.SetTrimRight(trim);
1422 for (
const auto& split: splits) {
1423 auto at = roundTime(warper->
Warp(split.time));
1424 for (
const auto &&clip : track.Intervals()) {
1428 if (clip->SplitsPlayRegion(at))
1430 auto newClip =
CopyClip(*clip,
true);
1431 clip->ClearRight(at);
1432 newClip->ClearLeft(at);
1435 attachRight(*clip, *split.left);
1438 attachLeft(*newClip, *split.right);
1439 track.InsertInterval(move(newClip),
false);
1442 else if (clip->GetPlayStartSample() ==
1443 track.TimeToLongSamples(at) && split.right) {
1445 const auto trim = clip->GetTrimLeft();
1446 const auto seqStartTime = clip->GetSequenceStartTime();
1447 clip->Clear(seqStartTime, seqStartTime + trim);
1450 clip->ShiftBy(trim);
1451 attachLeft(*clip, *split.right);
1454 else if (clip->GetPlayEndSample() ==
1455 track.TimeToLongSamples(at) && split.left) {
1458 clip->GetPlayEndTime(), clip->GetSequenceEndTime());
1459 attachRight(*clip, *split.left);
1466 for (
const auto& split : splits)
1468 auto s = track.TimeToLongSamples(warper->
Warp(split.time));
1469 for (
auto &&clip : track.Intervals()) {
1470 if (split.rightClipName.has_value() && clip->GetPlayStartSample() == s)
1471 clip->SetName(*split.rightClipName);
1472 else if (split.leftClipName.has_value() && clip->GetPlayEndSample() == s)
1473 clip->SetName(*split.leftClipName);
1478 for (
const auto &&clip : track.Intervals()) {
1479 const double st = clip->GetPlayStartTime();
1480 const double et = clip->GetPlayEndTime();
1483 for (
auto &cut : cuts) {
1488 double cs = cut->GetSequenceStartTime();
1492 if (cs >= st && cs <= et) {
1493 cut->SetSequenceStartTime(warper->
Warp(cs) - st);
1494 clip->AddCutLine(cut);
1505 constexpr bool addCutLines =
false;
1506 constexpr bool split =
true;
1513 const auto begin = clips.begin();
1514 const auto pred = [&](
auto pClip){
return pClip.get() == &clip; };
1515 auto iter = std::find_if(
begin, clips.end(), pred);
1516 return std::distance(
begin, iter);
1522 if (distance < clips.size())
1523 clips.erase(clips.begin() + distance);
1528 const bool split,
const bool clearByTrimming)
1532 wxASSERT( t1 >= t0 );
1546 if (clip->PartlyWithinPlayRegion(t0, t1)) {
1547 addCutLines =
false;
1552 if (clip->CoversEntirePlayRegion(t0, t1))
1554 clipsToDelete.push_back(clip);
1555 else if (clip->IntersectsPlayRegion(t0, t1)) {
1560 clipsToDelete.push_back(clip);
1561 auto newClip =
CopyClip(*clip,
true);
1562 newClip->ClearAndAddCutLine(t0, t1);
1563 clipsToAdd.push_back(move(newClip));
1566 if (split || clearByTrimming) {
1569 if (clip->BeforePlayRegion(t0)) {
1574 clipsToDelete.push_back(clip);
1575 auto newClip =
CopyClip(*clip,
true);
1576 newClip->TrimLeft(t1 - clip->GetPlayStartTime());
1580 newClip->ShiftBy(t0 - t1);
1581 clipsToAdd.push_back(move(newClip));
1583 else if (clip->AfterPlayRegion(t1)) {
1588 clipsToDelete.push_back(clip);
1589 auto newClip =
CopyClip(*clip,
true);
1590 newClip->TrimRight(clip->GetPlayEndTime() - t0);
1592 clipsToAdd.push_back(move(newClip));
1598 auto leftClip =
CopyClip(*clip,
true);
1599 leftClip->TrimRight(clip->GetPlayEndTime() - t0);
1600 clipsToAdd.push_back(move(leftClip));
1602 auto rightClip =
CopyClip(*clip,
true);
1603 rightClip->TrimLeft(t1 - clip->GetPlayStartTime());
1607 rightClip->ShiftBy(t0 - t1);
1608 clipsToAdd.push_back(move(rightClip));
1610 clipsToDelete.push_back(clip);
1618 clipsToDelete.push_back(clip);
1619 auto newClip =
CopyClip(*clip,
true);
1622 newClip->Clear(t0,t1);
1624 clipsToAdd.push_back(move(newClip));
1633 for (
const auto &clip: clipsToDelete)
1641 if (clip->AtOrBeforePlayRegion(t1))
1642 clip->ShiftBy(-(t1 - t0));
1644 for (
auto &clip: clipsToAdd)
1651 if (newT1 > oldT1 &&
1657 if (newT1 > oldT1) {
1664 const auto offset = newT1 - oldT1;
1667 if (clip->GetPlayStartTime() > oldT1 - (1.0 / rate))
1668 clip->ShiftBy(offset);
1675 const auto duration = newT1 - oldT1;
1677 tmp->InsertSilence(0.0, duration);
1682 else if (newT1 < oldT1)
1683 Clear(newT1, oldT1);
1691 if (!tempo.has_value())
1698 double t0,
const WaveTrack& other,
bool merge)
1700 const auto otherNChannels = other.
NChannels();
1708 const auto insertDuration = endTime;
1709 auto &track = *
this;
1734 t0 = track.SnapToSample(t0);
1738 const auto clipAtT0 = track.GetIntervalAtTime(t0);
1741 const auto pitchAndSpeedMatch =
1742 !clipAtT0 || (clipAtT0->HasEqualPitchAndSpeed(*otherFirstClip) &&
1743 clipAtT0->HasEqualPitchAndSpeed(*otherLastClip));
1747 const bool singleClipMode =
1749 std::abs(startTime) < track.LongSamplesToTime(1) * 0.5 &&
1750 pitchAndSpeedMatch && merge;
1752 const auto rate = track.GetRate();
1753 if (insertDuration != 0 && insertDuration < 1.0 / rate)
1762 auto pastingFromTempTrack = !other.
GetOwner();
1767 XO(
"There is not enough room available to paste the selection"),
1768 XO(
"Warning"),
"Error:_Insufficient_space_in_track"
1772 if (editClipCanMove) {
1773 if (!singleClipMode)
1780 for (
const auto& clip : track.Intervals())
1781 if (clip->GetPlayStartTime() > t0 - (1.0 / rate))
1782 clip->ShiftBy(insertDuration);
1788 const auto clipAtT0 = track.GetClipAtTime(t0);
1789 const auto t = clipAtT0 ? clipAtT0->GetPlayEndTime() : t0;
1790 if (!track.IsEmpty(t, t + insertDuration))
1791 throw notEnoughSpaceException;
1798 if (singleClipMode && merge) {
1803 for (
const auto& clip : track.Intervals()) {
1804 if (editClipCanMove) {
1805 if (clip->SplitsPlayRegion(t0)) {
1814 if (clip->WithinPlayRegion(t0))
1825 if (!editClipCanMove) {
1828 for (
const auto& clip : track.Intervals()) {
1829 if (clip->GetPlayStartTime() > insideClip->GetPlayStartTime() &&
1830 insideClip->GetPlayEndTime() + insertDuration >
1831 clip->GetPlayStartTime())
1834 throw notEnoughSpaceException;
1837 if (
auto pClip = other.
GetClip(0)) {
1841 assert(insideClip->GetStretchRatio() == pClip->GetStretchRatio());
1844 assert(insideClip->NChannels() == pClip->NChannels());
1845 bool success = insideClip->Paste(t0, *pClip);
1856 if (!editClipCanMove &&
1857 !track.IsEmpty(t0, t0 + insertDuration - 1.0 / rate))
1860 throw notEnoughSpaceException;
1862 for (
const auto& clip : other.
Intervals()) {
1864 if (!clip->GetIsPlaceholder()) {
1865 const auto name = (pastingFromTempTrack)
1868 ? track.MakeNewClipName()
1869 : track.MakeClipCopyName(clip->GetName());
1870 const auto oldPlayStart = clip->GetPlayStartTime();
1871 const auto newSequenceStart =
1872 (oldPlayStart + t0) - clip->GetTrimLeft();
1873 const auto newClip =
CreateClip(newSequenceStart,
name, clip.get());
1874 newClip->Resample(rate);
1875 track.InsertInterval(move(newClip),
false);
1883 std::optional<double> oRate;
1885 return std::all_of(channels.begin(), channels.end(),
1890 const auto rate = pTrack->mLegacyRate;
1893 else if (*oRate != rate)
1902 return std::all_of(channels.begin(), channels.end(),
1904 return pTrack && pTrack->mLegacyFormat == mLegacyFormat;
1909 bool newClip,
bool backup,
bool allowEmpty)
1911 if (!backup && !clip->GetIsPlaceholder() && !allowEmpty && clip->IsEmpty())
1915 if (tempo.has_value())
1916 clip->OnProjectTempoChange(std::nullopt, *tempo);
1917 clips.push_back(std::move(clip));
1929 assert(!interval.has_value() ||
1930 interval->first <= interval->second);
1933 const auto startTime =
1936 const auto endTime =
1939 if (startTime >= endTime)
1944 clipAtT0 && clipAtT0->SplitsPlayRegion(startTime) &&
1945 clipAtT0->HasPitchOrSpeed())
1946 Split(startTime, startTime);
1948 clipAtT1 && clipAtT1->SplitsPlayRegion(endTime) &&
1949 clipAtT1->HasPitchOrSpeed())
1950 Split(endTime, endTime);
1954 while (clip && clip->GetPlayStartTime() < endTime)
1956 if (clip->HasPitchOrSpeed())
1957 srcIntervals.push_back(clip);
1967 if (
const auto other =
dynamic_cast<const WaveTrack*
>(&src))
1971 constexpr auto merge =
true;
1990 auto clipStart = pClip->GetPlayStartSample();
1991 auto clipEnd = pClip->GetPlayEndSample();
1992 if (clipEnd > start && clipStart <
end) {
1993 auto offset = std::max(start - clipStart,
sampleCount(0));
1995 auto length =
std::min(
end, clipEnd) - (clipStart + offset);
1996 pClip->SetSilence(offset, length);
2012 if (clips.empty()) {
2015 clip->InsertSilence(0, len);
2022 const auto end = clips.end();
2023 const auto it = std::find_if(clips.begin(),
end,
2024 [&](
const IntervalHolder &clip) { return clip->SplitsPlayRegion(t); } );
2028 (*it)->InsertSilence(t, len);
2031 for (
const auto &&clip : clips)
2032 if (clip->BeforePlayRegion(t))
2043 const size_t maxAtOnce = 1048576;
2044 std::vector<float> buffer;
2045 std::vector<samplePtr> buffers;
2050 for (
const auto &interval :
Intervals()) {
2051 double startTime = interval->Start();
2052 double endTime = interval->End();
2054 if (endTime < t0 || startTime > t1)
2058 if (buffer.empty()) {
2059 buffer.resize(maxAtOnce * width);
2060 buffers.resize(width);
2061 auto pBuffer = buffer.data();
2062 for (
size_t ii = 0; ii < width; ++ii, pBuffer += maxAtOnce)
2063 buffers[ii] =
reinterpret_cast<samplePtr>(pBuffer);
2066 const auto allZeroesAt = [&](
size_t i) {
2067 auto pData = buffer.data() + i;
2068 for (
size_t ii = 0; ii < width; ++ii, pData += maxAtOnce) {
2079 auto start = interval->TimeToSamples(std::max(.0, t0 - startTime));
2080 auto end = interval->TimeToSamples(
std::min(endTime, t1) - startTime);
2082 auto len = (
end - start);
2083 for (
decltype(len) done = 0; done < len; done += maxAtOnce) {
2086 auto bufferIt = buffers.begin();
2088 for (
auto channel : interval->Channels())
2089 channel->GetSamples(
2090 *bufferIt++,
floatSample, start + done, numSamples);
2092 for (
decltype(numSamples) i = 0; i < numSamples; ++i) {
2093 auto curSamplePos = start + done + i;
2096 if (seqStart == -1 && allZeroesAt(i))
2097 seqStart = curSamplePos;
2098 else if (curSamplePos ==
end - 1 || !allZeroesAt(i)) {
2099 if (seqStart != -1) {
2100 decltype(
end) seqEnd;
2103 if (curSamplePos ==
end - 1 && allZeroesAt(i))
2106 seqEnd = curSamplePos;
2107 if (seqEnd - seqStart + 1 > minSamples) {
2110 startTime + interval->SamplesToTime(seqStart),
2111 startTime + interval->SamplesToTime(seqEnd)
2122 for (
const auto ®ion : regions)
2135 for (
const auto &interval : intervals)
2136 if (interval->IntersectsPlayRegion(t0, t1))
2137 intervalsToJoin.push_back(interval);
2138 if (intervalsToJoin.size() < 2u)
2141 intervalsToJoin.begin() + 1, intervalsToJoin.end(),
2142 [first = intervalsToJoin[0]](
const auto& interval) {
2143 return !first->HasEqualPitchAndSpeed(*interval);
2152 for (
const auto &clip: intervals) {
2153 if (clip->IntersectsPlayRegion(t0, t1)) {
2155 auto it = clipsToDelete.begin(),
end = clipsToDelete.end();
2156 for (; it !=
end; ++it)
2157 if ((*it)->GetPlayStartTime() > clip->GetPlayStartTime())
2160 clipsToDelete.insert(it, clip);
2165 if (clipsToDelete.empty())
2168 const auto firstToDelete = clipsToDelete[0].get();
2169 auto t = firstToDelete->GetPlayStartTime();
2172 firstToDelete->GetSequenceStartTime(),
2173 firstToDelete->GetName());
2175 for (
const auto &clip : clipsToDelete) {
2180 if (clip->GetPlayStartTime() - t > (1.0 / rate))
2182 double addedSilence = (clip->GetPlayStartTime() - t);
2184 auto offset = clip->GetPlayStartTime();
2185 auto value = clip->GetEnvelope().GetValue(offset);
2186 newClip->AppendSilence(addedSilence, value);
2191 bool success = newClip->Paste(t, *clip);
2194 t = newClip->GetPlayEndTime();
2206 size_t len,
unsigned stride,
sampleFormat effectiveFormat)
2229 size_t len,
unsigned int stride,
sampleFormat effectiveFormat)
2236 buffers,
format, len, stride, effectiveFormat);
2244 auto startSample = clip->GetPlayStartSample();
2245 auto endSample = clip->GetPlayEndSample();
2246 if (s >= startSample && s < endSample)
2250 clip->GetBestBlockSize(s - clip->GetSequenceStartSample());
2255 return bestBlockSize;
2261 auto maxblocksize = std::accumulate(clips.begin(), clips.end(),
size_t{},
2262 [](
size_t acc,
auto pClip){
2263 return std::max(acc, pClip->GetMaxBlockSize()); });
2265 if (maxblocksize == 0)
2274 wxASSERT(maxblocksize > 0);
2276 return maxblocksize;
2283 .GetSequence(0)->GetIdealBlockSize();
2305 pInterval->RepairChannels();
2344 for (
const auto& pair : attrs)
2346 const auto& attr = pair.first;
2347 const auto& value = pair.second;
2352 if (!value.TryGet(dblValue) ||
2353 (dblValue < 1.0) || (dblValue > 1000000.0))
2359 else if (attr ==
Offset_attr && value.TryGet(dblValue))
2370 else if (attr ==
Gain_attr && value.TryGet(dblValue))
2372 else if (attr ==
Pan_attr && value.TryGet(dblValue) &&
2373 (dblValue >= -1.0) && (dblValue <= 1.0))
2375 else if (attr ==
Linked_attr && value.TryGet(nValue))
2408 const auto getClip = [
this]() ->
WaveClip & {
2420 return getClip().GetSequence(0);
2421 else if (tag ==
"envelope")
2422 return &getClip().GetEnvelope();
2430 auto pSeq = getClip().GetSequence(0);
2440 auto clip = std::make_shared<WaveClip>(1,
2442 const auto xmlHandler = clip.get();
2444 clips.push_back(std::move(clip));
2455 const auto channels = Channels();
2457 nChannels = channels.size();
2458 for (
const auto pChannel : channels)
2459 WriteOneXML(*pChannel, xmlFile,
iChannel++, nChannels);
2477 track.Track::WriteCommonXMLAttributes(xmlFile);
2487 const auto channelType = (nChannels == 0)
2497 const auto linkType =
static_cast<int>(
2498 (
iChannel == 0) && (nChannels == 2)
2504 track.WritableSampleTrack::WriteXMLAttributes(xmlFile);
2515 for (
const auto &clip : channel.
Intervals())
2516 clip->WriteXML(xmlFile);
2524 const auto width = pClip->NChannels();
2525 for (
size_t ii = 0; ii < width; ++ii)
2526 if (pClip->GetSequence(ii)->GetErrorOpening())
2527 return XO(
"A track has a corrupted sample sequence.");
2534 auto clips = Intervals();
2537 const auto begin = clips.begin(),
2538 iter = std::min_element(
begin, clips.end(),
2539 [](
const auto& a,
const auto b) {
2540 return a->GetPlayStartTime() < b->GetPlayStartTime();
2542 return GetClip(std::distance(
begin, iter));
2550 auto clips = Intervals();
2553 const auto begin = clips.begin(),
2554 iter = std::max_element(
begin, clips.end(),
2555 [](
const auto& a,
const auto b) {
2556 return a->GetPlayEndTime() < b->GetPlayEndTime();
2558 return GetClip(std::distance(
begin, iter));
2568 return { clips.begin(), clips.end() };
2594 bool mayThrow,
sampleCount* pNumWithinClips)
const
2599 assert(nBuffers <= 1);
2601 buffers,
format, start, len, backwards, fill, mayThrow, pNumWithinClips);
2612 bool mayThrow,
sampleCount* pNumWithinClips)
const
2615 assert(
iChannel + nBuffers <= nChannels);
2616 return std::all_of(buffers, buffers + nBuffers, [&](
samplePtr buffer) {
2618 buffer,
format, start, len, backwards, fill, mayThrow,
2626 bool backwards,
fillFormat fill,
bool mayThrow,
2634 bool doClear =
true;
2637 for (
const auto &clip: clips)
2639 if (start >= clip->GetPlayStartSample() && start+len <= clip->GetPlayEndSample())
2654 float * pBuffer = (
float*)buffer;
2655 for(
size_t i=0;i<len;i++)
2660 wxFAIL_MSG(
wxT(
"Invalid fill format"));
2665 for (
const auto &clip: clips)
2667 auto clipStart = clip->GetPlayStartSample();
2668 auto clipEnd = clip->GetPlayEndSample();
2670 if (clipEnd > start && clipStart < start+len)
2672 if (clip->HasPitchOrSpeed())
2676 auto samplesToCopy =
2677 std::min( start+len - clipStart, clip->GetVisibleSampleCount() );
2678 auto startDelta = clipStart - start;
2679 decltype(startDelta) inclipDelta = 0;
2682 inclipDelta = -startDelta;
2683 samplesToCopy -= inclipDelta;
2699 startDelta.as_size_t() *
2701 format, inclipDelta, samplesToCopy.as_size_t(), mayThrow ))
2704 samplesCopied += samplesToCopy;
2707 if( pNumWithinClips )
2708 *pNumWithinClips = samplesCopied;
2709 if (result ==
true && backwards)
2718 for (
const auto& channel :
Channels()) {
2719 result.push_back(channel->GetSampleView(t0, t1, mayThrow));
2727 std::vector<std::shared_ptr<const WaveClipChannel>>
2728 intersectingIntervals;
2729 for (
const auto &interval :
Intervals())
2730 if (interval->Intersects(t0, t1))
2731 intersectingIntervals.push_back(interval);
2732 if (intersectingIntervals.empty())
2736 intersectingIntervals.begin(), intersectingIntervals.end(),
2737 [](
const auto& a,
const auto& b) { return a->Start() < b->Start(); });
2738 std::vector<AudioSegmentSampleView> segments;
2739 segments.reserve(2 * intersectingIntervals.size() + 1);
2740 for (
auto i = 0u; i < intersectingIntervals.size();++i)
2742 const auto& interval = intersectingIntervals[i];
2743 const auto intervalStartTime = interval->Start();
2744 if (t0 < intervalStartTime)
2748 t0 = intervalStartTime;
2750 const auto intervalT0 = t0 - intervalStartTime;
2751 const auto intervalT1 =
std::min(t1, interval->End()) - intervalStartTime;
2752 if(intervalT1 > intervalT0)
2755 interval->GetSampleView(intervalT0, intervalT1, mayThrow);
2756 t0 += intervalT1 - intervalT0;
2757 segments.push_back(std::move(newSegment));
2774 auto clipStart = clip->GetPlayStartSample();
2775 auto clipEnd = clip->GetPlayEndSample();
2777 if (clipEnd > start && clipStart < start+len)
2780 if (clip->HasPitchOrSpeed())
2784 auto samplesToCopy =
2785 std::min( start+len - clipStart, clip->GetVisibleSampleCount() );
2786 auto startDelta = clipStart - start;
2787 decltype(startDelta) inclipDelta = 0;
2790 inclipDelta = -startDelta;
2791 samplesToCopy -= inclipDelta;
2807 format, inclipDelta, samplesToCopy.as_size_t(), effectiveFormat );
2822 result = std::max(result, pClip->GetSampleFormats().Effective());
2836 auto clips = pTrack->Intervals();
2837 return std::all_of(clips.begin(), clips.end(),
2838 [](
const auto &pClip){ return pClip->GetEnvelope().IsTrivial(); });
2842 double* buffer,
size_t bufferLen,
double t0,
bool backwards)
const
2848 double* buffer,
size_t bufferLen,
double t0,
bool backwards)
const
2855 t0 -= bufferLen / pTrack->GetRate();
2866 for (
decltype(bufferLen) i = 0; i < bufferLen; i++)
2871 double startTime = t0;
2872 const auto rate = pTrack->GetRate();
2873 auto tstep = 1.0 / rate;
2874 double endTime = t0 + tstep * bufferLen;
2875 for (
const auto &clip: pTrack->Intervals())
2878 auto dClipStartTime = clip->GetPlayStartTime();
2879 auto dClipEndTime = clip->GetPlayEndTime();
2880 if ((dClipStartTime < endTime) && (dClipEndTime > startTime))
2883 auto rlen = bufferLen;
2886 if (rt0 < dClipStartTime)
2890 auto nDiff = (
sampleCount)floor((dClipStartTime - rt0) * rate + 0.5);
2891 auto snDiff = nDiff.as_size_t();
2893 wxASSERT(snDiff <= rlen);
2895 rt0 = dClipStartTime;
2898 if (rt0 + rlen*tstep > dClipEndTime)
2900 auto nClipLen = clip->GetPlayEndSample() - clip->GetPlayStartSample();
2911 rlen =
std::min(rlen,
size_t(floor(0.5 + (dClipEndTime - rt0) / tstep)));
2915 clip->GetEnvelope().GetValues(rbuf, rlen, rt0, tstep);
2919 std::reverse(buffer, buffer + bufferLen);
2927 auto p = std::find_if(
2928 clips.rbegin(), clips.rend(), [&](
const auto &pClip) {
2929 return pClip->WithinPlayRegion(time);
2931 return p != clips.rend() ? *p :
nullptr;
2939 std::make_shared<WaveClip>(*pToCopy, mpFactory, copyCutlines);
2940 pNewClip->SetName(
name);
2941 pNewClip->SetSequenceStartTime(offset);
2945 return DoCreateClip(offset,
name);
2951 return CreateClip(toCopy.GetSequenceStartTime(),
2952 toCopy.GetName(), &toCopy, copyCutlines);
2963 auto clip = std::make_shared<WaveClip>(NChannels(),
2964 mpFactory, GetSampleFormat(),
GetRate());
2965 clip->SetName(
name);
2966 clip->SetSequenceStartTime(offset);
2969 if (tempo.has_value())
2970 clip->OnProjectTempoChange(std::nullopt, *tempo);
2971 assert(clip->NChannels() == NChannels());
2977 const auto &intervals = Intervals();
2978 if (intervals.empty()) {
2980 const auto name = MakeNewClipName();
2981 auto pInterval = CreateClip(origin,
name);
2982 InsertInterval(pInterval,
true,
true);
2986 return mClips.back();
2992 if (mClips.empty()) {
2993 auto pInterval = CreateClip(
2995 InsertInterval(pInterval,
true,
true);
2999 auto end = mClips.end(),
3000 it = max_element(mClips.begin(),
end,
3001 [](
const auto &pClip1,
const auto &pClip2){
3002 return pClip1->GetPlayStartTime() < pClip2->GetPlayStartTime();
3015 [&](
const auto &pOtherClip){
return &clip == pOtherClip.get(); };
3016 auto begin = clips.begin(),
3018 iter = std::find_if(
begin,
end, test);
3019 return std::distance(
begin, iter);
3028 const std::vector<Interval*> &movingClips,
3030 double *allowedAmount )
3033 *allowedAmount = amount;
3035 const auto &moving = [&](
Interval *clip){
3038 return movingClips.end() !=
3039 std::find(movingClips.begin(), movingClips.end(), clip);
3043 if ( moving( c.get() ) )
3045 for (
const auto clip : movingClips) {
3046 if (c->GetPlayStartTime() < clip->GetPlayEndTime() + amount &&
3047 c->GetPlayEndTime() > clip->GetPlayStartTime() + amount)
3054 if (c->GetPlayStartTime() - clip->GetPlayEndTime() < *allowedAmount)
3055 *allowedAmount = c->GetPlayStartTime() - clip->GetPlayEndTime();
3056 if (*allowedAmount < 0)
3060 if (c->GetPlayEndTime() - clip->GetPlayStartTime() > *allowedAmount)
3061 *allowedAmount = c->GetPlayEndTime() - clip->GetPlayStartTime();
3062 if (*allowedAmount > 0)
3071 if (*allowedAmount == amount)
3087 const Interval& candidateClip,
double& slideBy,
double tolerance)
const
3093 const auto candidateClipStartTime = candidateClip.GetPlayStartTime();
3094 const auto candidateClipEndTime = candidateClip.GetPlayEndTime();
3095 const auto t0 =
SnapToSample(candidateClipStartTime + slideBy);
3096 const auto t1 =
SnapToSample(candidateClipEndTime + slideBy);
3097 std::vector<double> overlaps;
3099 clips.begin(), clips.end(), std::back_inserter(overlaps),
3100 [&](
const auto& pClip) {
3101 return pClip->IntersectsPlayRegion(t0, t1) ?
3102 std::min(pClip->GetPlayEndTime(), t1) -
3103 std::max(pClip->GetPlayStartTime(), t0) :
3106 const auto maxOverlap = std::max_element(overlaps.begin(), overlaps.end());
3107 if (*maxOverlap > tolerance)
3109 auto iter = clips.begin();
3110 std::advance(iter, std::distance(overlaps.begin(), maxOverlap));
3111 const auto overlappedClip = *iter;
3112 const auto requiredOffset = slideBy +
3113 *maxOverlap * (overlappedClip->GetPlayStartTime() < t0 ? 1 : -1);
3116 clips.begin(), clips.end(),
3117 [&](
const auto& pClip)
3119 const auto result = pClip->IntersectsPlayRegion(
3120 SnapToSample(candidateClipStartTime + requiredOffset),
3121 SnapToSample(candidateClipEndTime + requiredOffset));
3125 slideBy = requiredOffset;
3140 for (
const auto &&c : Intervals()) {
3141 if (c->SplitsPlayRegion(t)) {
3142 t = SnapToSample(t);
3143 auto newClip = CopyClip(*c,
true);
3145 newClip->TrimLeftTo(t);
3146 auto result = std::pair{ c, newClip };
3150 InsertInterval(move(newClip),
false);
3160 const auto clip1 =
GetClip(clipidx1);
3161 const auto clip2 =
GetClip(clipidx2);
3163 if (!clip1 || !clip2)
3166 if (!clip1->HasEqualPitchAndSpeed(*clip2))
3171 bool success = clip1->Paste(clip1->GetPlayEndTime(), *clip2);
3185 dstIntervals.reserve(srcIntervals.size());
3187 srcIntervals.begin(), srcIntervals.end(),
3188 std::back_inserter(dstIntervals), [&](
const IntervalHolder& interval) {
3189 return GetRenderedCopy(interval,
3190 reportProgress, mpFactory, GetSampleFormat());
3195 for (
auto i = 0; i < srcIntervals.size(); ++i)
3203 using Set = std::unordered_set<WaveClipHolder>;
3204 return clips.size() == Set{ clips.begin(), clips.end() }.size();
3209 bool newClip,
bool allowEmpty)
3212 constexpr bool backup =
false;
3222 iter = find(
mClips.begin(),
end, interval);
3231 assert(oldOne->NChannels() == newOne->NChannels());
3234 newOne->SetName(oldOne->GetName());
3242 pClip->Resample(rate, progress);
3251 for (
const auto &pChannel :
Channels()) {
3252 const auto buffer = buffers[ii++];
3254 result = pChannel->SetFloats(buffer, start, len, effectiveFormat)
3264 const auto comp = [](
const auto &a,
const auto &b) {
3265 return a->GetPlayStartTime() < b->GetPlayStartTime(); };
3266 std::sort(clips.begin(), clips.end(), comp);
3272 const auto &intervals = Intervals();
3274 copy(intervals.begin(), intervals.end(), back_inserter(result));
3275 sort(result.begin(), result.end(), [](
const auto &pA,
const auto &pB){
3276 return pA->GetPlayStartTime() < pB->GetPlayStartTime(); });
3284 copy(intervals.begin(), intervals.end(), back_inserter(result));
3285 sort(result.begin(), result.end(), [](
const auto &pA,
const auto &pB){
3286 return pA->GetPlayStartTime() < pB->GetPlayStartTime(); });
3299 auto iter = pOwner->Find(
this);
3300 assert(
this == *iter);
3302 assert(iter != pOwner->end());
3303 auto pRight =
dynamic_cast<WaveTrack*
>(*iter);
3304 assert(pRight && pRight->NChannels() == 1);
3317 auto iterMe =
mClips.begin(),
3319 auto iterRight = pRight->mClips.begin(),
3320 endRight = pRight->mClips.end();
3321 while (iterMe != endMe && iterRight != endRight) {
3322 (*iterMe)->MakeStereo(std::move(**iterRight), mustAlign);
3326 assert(!mustAlign || (iterMe == endMe && iterRight == endRight));
3328 while (iterRight != endRight) {
3330 mClips.emplace_back(move(*iterRight));
3336 pOwner->Remove(*pRight);
3340 return std::make_shared< WaveTrackFactory >(
3372 L
"/GUI/TrackNames/DefaultTrackName",
3386 bool editClipsCanMove;
3391 L
"/GUI/EditClipCanMove",
false };
@ BadUserAction
Indicates that the user performed an action that is not allowed.
std::vector< std::shared_ptr< const ClipInterface > > ClipConstHolders
An audio segment is either a whole clip or the silence between clips. Views allow shared references t...
std::vector< AudioSegmentSampleView > ChannelSampleView
Toolkit-neutral facade for basic user interface services.
Adapts TrackAttachment interface with extra channel index argument.
EffectDistortionSettings params
const TranslatableString name
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
std::vector< std::vector< AudioSegmentSampleView > > ChannelGroupSampleView
an object holding per-project preferred sample rate
std::shared_ptr< SampleBlockFactory > SampleBlockFactoryPtr
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
BoolSetting SyncLockTracks
const std::optional< double > & GetProjectTempo(const ChannelGroup &group)
void DoProjectTempoChange(ChannelGroup &group, double newTempo)
Contains declarations for TimeWarper, IdentityTimeWarper, ShiftTimeWarper, LinearTimeWarper,...
std::function< void(double)> ProgressReporter
std::shared_ptr< TrackList > TrackListHolder
std::shared_ptr< WaveClip > WaveClipHolder
std::vector< WaveClipHolder > WaveClipHolders
bool GetEditClipsCanMove()
static constexpr auto Pan_attr
static ProjectFileIORegistry::ObjectReaderEntry readerEntry
static constexpr auto Rate_attr
static const AudacityProject::AttachedObjects::RegisteredFactory key2
DEFINE_XML_METHOD_REGISTRY(WaveTrackIORegistry)
static constexpr auto SampleFormat_attr
BoolSetting EditClipsCanMove
static constexpr auto Channel_attr
static constexpr auto Offset_attr
static constexpr auto Linked_attr
static auto TrackFactoryFactory
static constexpr auto Gain_attr
StringSetting AudioTrackNameSetting
static const Track::TypeInfo & typeInfo()
#define WAVETRACK_MERGE_POINT_TOLERANCE
std::vector< Attribute > AttributesList
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Abstraction of a progress dialog with well defined time-to-completion estimate.
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Holds multiple objects as a single attachment to Track.
double GetEndTime() const
Get the maximum of End() values of intervals, or 0 when none.
double GetStartTime() const
Get the minimum of Start() values of intervals, or 0 when none.
LinkType
For two tracks describes the type of the linkage.
@ Group
compatibility with projects that were generated by older versions of Audacity
@ Aligned
Tracks are grouped and changes should be synchronized.
virtual double Start() const =0
size_t GetChannelIndex() const
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
size_t size() const
How many attachment pointers are in the Site.
void ForCorresponding(Site &other, const Function &function, bool create=true)
void ForEach(const Function &function)
Invoke function on each ClientData object that has been created in this.
No change to time at all.
CallbackReturn Publish(const WaveTrackMessage &message)
Send a message to connected callbacks.
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
static ProjectRate & Get(AudacityProject &project)
static SampleBlockFactoryPtr New(AudacityProject &project)
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
static const char * Sequence_tag
static const char * WaveBlock_tag
static bool IsValidSampleFormat(const int nValue)
true if nValue is one of the sampleFormat enum values
bool ReadWithDefault(T *pVar, const T &defaultValue) const
overload of ReadWithDefault returning a boolean that is true if the value was previously defined */
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined */
A MessageBoxException that shows a given, unvarying string.
Specialization of Setting for strings.
Transforms one point in time to another point. For example, a time stretching effect might use one to...
virtual double Warp(double originalTime) const =0
Abstract base class for an object holding data associated with points on a time axis.
void Notify(bool allChannels, int code=-1)
std::shared_ptr< TrackList > GetOwner() const
static void CopyAttachments(Track &dst, const Track &src, bool deep)
Copy (deep) or just share (!deep) AttachedTrackObjects.
virtual bool LinkConsistencyFix(bool doFix=true)
Check consistency of channel groups, and maybe fix it.
void SetLinkType(LinkType linkType, bool completeList=true)
std::shared_ptr< Track > Holder
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
const wxString & GetName() const
Name is always the same for all channels of a group.
void Init(const Track &orig)
LinkType GetLinkType() const noexcept
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
static TrackList & Get(AudacityProject &project)
TrackKind * Add(const std::shared_ptr< TrackKind > &t, bool assignIds=true)
static TrackListHolder Temporary(AudacityProject *pProject, const Track::Holder &pTrack={})
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
ChannelSampleView GetSampleView(double t0, double t1, bool mayThrow) const
Request channel samples within [t0, t1), not knowing in advance how many this will be.
AudioGraph::ChannelType GetChannelType() const override
Classify this channel.
double GetRate() const override
double GetStartTime() const override
double GetEndTime() const override
size_t NChannels() const override
A constant property.
bool HasTrivialEnvelope() const override
float GetChannelGain(int channel) const override
Takes gain and pan into account.
void GetEnvelopeValues(double *buffer, size_t bufferLen, double t0, bool backwards) const override
IteratorRange< IntervalIterator< WaveClipChannel > > Intervals()
bool Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Random-access assignment of a range of samples.
bool DoGet(size_t iChannel, size_t nBuffers, const samplePtr buffers[], sampleFormat format, sampleCount start, size_t len, bool backwards, fillFormat fill=FillFormat::fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const override
This fails if any clip overlapping the range has non-unit stretch ratio!
std::shared_ptr< WaveClipChannel > GetInterval(size_t iInterval)
bool Append(constSamplePtr buffer, sampleFormat format, size_t len)
sampleFormat WidestEffectiveFormat() const override
bool AppendBuffer(constSamplePtr buffer, sampleFormat format, size_t len, unsigned stride, sampleFormat effectiveFormat)
ChannelGroup & DoGetChannelGroup() const override
Subclass must override.
WaveChannel(WaveTrack &owner)
This allows multiple clips to be a part of one WaveTrack.
static const char * WaveClip_tag
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
static void Destroy(AudacityProject &project)
std::shared_ptr< WaveTrack > DoCreate(size_t nChannels, sampleFormat format, double rate)
static WaveTrackFactory & Get(AudacityProject &project)
TrackListHolder CreateMany(size_t nChannels)
Creates tracks with project's default rate and format and the given number of channels.
static WaveTrackFactory & Reset(AudacityProject &project)
SampleBlockFactoryPtr mpFactory
const ProjectRate & mRate
A Track that contains audio waveform data.
void HandleXMLEndTag(const std::string_view &tag) override
void MakeMono()
Simply discard any right channel.
SampleBlockFactoryPtr mpFactory
bool MergeClips(int clipidx1, int clipidx2)
bool GetMute() const override
May vary asynchronously.
void SetRate(double newRate)
!brief Sets the new rate for the track without resampling it
IntervalHolder RightmostOrNewClip()
Get access to the last (rightmost) clip, or create a clip, if there is not already one.
void CopyClips(WaveClipHolders &clips, SampleBlockFactoryPtr pFactory, const WaveClipHolders &orig, bool backup)
IntervalConstHolders SortedClipArray() const
Return all WaveClips sorted by clip play start time.
void DoSetPan(float value)
static WaveTrack * New(AudacityProject &project)
void RemoveClip(std::ptrdiff_t distance)
bool GetOne(const WaveClipHolders &clips, size_t iChannel, samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool backwards, fillFormat fill, bool mayThrow, sampleCount *pNumWithinClips) const
void SplitDelete(double t0, double t1)
std::vector< Region > Regions
bool HasClipNamed(const wxString &name) const
void Silence(double t0, double t1, ProgressReporter reportProgress) override
bool DoGet(size_t iChannel, size_t nBuffers, const samplePtr buffers[], sampleFormat format, sampleCount start, size_t len, bool backwards, fillFormat fill=FillFormat::fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const override
This fails if any clip overlapping the range has non-unit stretch ratio!
IntervalHolder NewestOrNewClip()
Get access to the most recently added clip, or create a clip, if there is not already one....
std::ptrdiff_t FindClip(const Interval &clip)
std::vector< Holder > SplitChannels()
double GetStartTime() const override
Implement WideSampleSequence.
void ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
void ApplyPitchAndSpeedOnIntervals(const std::vector< IntervalHolder > &intervals, const ProgressReporter &reportProgress)
int mLegacyRate
used only during deserialization
void InsertSilence(double t, double len) override
IntervalHolder CreateClip(double offset=.0, const wxString &name=wxEmptyString, const Interval *pToCopy=nullptr, bool copyCutlines=true)
bool Append(size_t iChannel, constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1, sampleFormat effectiveFormat=widestSampleFormat) override
Track::Holder PasteInto(AudacityProject &project, TrackList &list) const override
void WriteXML(XMLWriter &xmlFile) const override
wxString MakeNewClipName() const
size_t NIntervals() const override
Report the number of intervals.
static Holder Create(const SampleBlockFactoryPtr &pFactory, sampleFormat format, double rate)
Factory builds all AttachedTrackObjects.
void InsertInterval(const IntervalHolder &interval, bool newClip, bool allowEmpty=false)
static wxString GetDefaultAudioTrackNamePreference()
bool LinkConsistencyFix(bool doFix) override
Check consistency of channel groups, and maybe fix it.
IntervalConstHolder GetNextInterval(const Interval &interval, PlaybackDirection searchDirection) const
std::optional< TranslatableString > GetErrorOpening() const override
std::shared_ptr<::Channel > DoGetChannel(size_t iChannel) override
void ZipClips(bool mustAlign=true)
sampleFormat GetSampleFormat() const override
void Join(double t0, double t1, const ProgressReporter &reportProgress)
IntervalConstHolder GetClipAtTime(double time) const
void ClearAndAddCutLine(double t0, double t1)
void SyncLockAdjust(double oldT1, double newT1) override
const TypeInfo & GetTypeInfo() const override
bool SetFloats(const float *const *buffers, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Random-access assignment of a range of samples.
size_t GetIdealBlockSize()
Track::Holder Cut(double t0, double t1) override
Create tracks and modify this track.
void CopyWholeClip(const Interval &clip, double t0, bool forClipboard)
bool InsertClip(WaveClipHolders &clips, WaveClipHolder clip, bool newClip, bool backup, bool allowEmpty)
ChannelGroupSampleView GetSampleView(double t0, double t1, bool mayThrow=true) const
Request samples within [t0, t1), not knowing in advance how many this will be.
void Clear(double t0, double t1) override
void Init(const WaveTrack &orig)
void DoSetRate(double newRate)
std::shared_ptr< Interval > IntervalHolder
void Split(double t0, double t1)
Track::Holder Clone(bool backup) const override
std::pair< IntervalHolder, IntervalHolder > SplitAt(double t)
IntervalHolders SortedIntervalArray()
Return all WaveClips sorted by clip play start time.
void ReplaceInterval(const IntervalHolder &oldOne, const IntervalHolder &newOne)
static const char * WaveTrack_tag
void FinishCopy(double t0, double t1, double endTime, bool forClipboard)
static void WriteOneXML(const WaveChannel &channel, XMLWriter &xmlFile, size_t iChannel, size_t nChannels)
const SampleBlockFactoryPtr & GetSampleBlockFactory() const
int GetClipIndex(const Interval &clip) const
Get the linear index of a given clip (== number of clips if not found)
bool IsEmpty(double t0, double t1) const
Returns true if there are no WaveClips in the specified region.
Holder DuplicateWithOtherTempo(double newTempo) const
std::vector< IntervalHolder > IntervalHolders
sampleFormat WidestEffectiveFormat() const override
void SetPan(float newPan)
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
std::optional< WaveChannel > mRightChannel
may be null
static const TypeInfo & ClassTypeInfo()
WaveClipHolders & NarrowClips()
void Paste(double t0, const Track &src) override
void MoveTo(double o) override
void SetLegacyFormat(sampleFormat format)
void ApplyPitchAndSpeed(std::optional< TimeInterval > interval, ProgressReporter reportProgress)
void Trim(double t0, double t1)
void Disjoin(double t0, double t1)
void DoSetGain(float value)
void Resample(int rate, BasicUI::ProgressDialog *progress=NULL)
IntervalHolder CopyClip(const Interval &toCopy, bool copyCutlines)
Create new clip and add it to this track.
void RepairChannels() override
double GetEndTime() const override
Implement WideSampleSequence.
void CopyPartOfClip(const Interval &clip, double t0, double t1, bool forClipboard)
std::shared_ptr< WideChannelGroupInterval > DoGetInterval(size_t iInterval) override
Retrieve an interval.
AudioGraph::ChannelType GetChannelType() const override
Classify this channel.
bool FormatConsistencyCheck() const
Holder SplitCut(double t0, double t1)
void SetGain(float newGain)
double GetRate() const override
void ClearAndPasteAtSameTempo(double t0, double t1, const WaveTrack &src, bool preserve, bool merge, const TimeWarper *effectWarper, bool clearByTrimming)
bool RateConsistencyCheck() const
Whether all clips of an unzipped leader track have a common rate.
bool GetSolo() const override
May vary asynchronously.
float GetChannelGain(int channel) const override
Takes gain and pan into account.
const ChannelGroup * FindChannelGroup() const override
Find associated ChannelGroup if any.
std::vector< IntervalConstHolder > IntervalConstHolders
ClipConstHolders GetClipInterfaces() const
Get access to the (visible) clips in the tracks, in unspecified order.
size_t NChannels() const override
A constant property.
bool CanOffsetClips(const std::vector< Interval * > &movingClips, double amount, double *allowedAmount=nullptr)
Decide whether the clips could be offset (and inserted) together without overlapping other clips.
size_t GetMaxBlockSize() const
void PasteWaveTrackAtSameTempo(double t0, const WaveTrack &other, bool merge)
void GetEnvelopeValues(double *buffer, size_t bufferLen, double t0, bool backwards) const override
bool HasTrivialEnvelope() const override
void EraseChannelAttachments(size_t index)
Erase all attachments for a given index.
void PasteWaveTrack(double t0, const WaveTrack &other, bool merge)
double mLegacyProjectFileOffset
size_t GetBestBlockSize(sampleCount t) const
sampleFormat mLegacyFormat
used only during deserialization
Holder EmptyCopy(size_t nChannels, const SampleBlockFactoryPtr &pFactory={}) const
std::shared_ptr< WaveTrack > Holder
static double ProjectNyquistFrequency(const AudacityProject &project)
void MergeChannelAttachments(WaveTrack &&other)
IntervalHolder GetRightmostClip()
bool CanInsertClip(const Interval &clip, double &slideBy, double tolerance) const
WaveTrack(CreateToken &&, const SampleBlockFactoryPtr &pFactory, sampleFormat format, double rate)
Don't call directly, but use Create.
void ClearAndPaste(double t0, double t1, const WaveTrack &src, bool preserve=true, bool merge=true, const TimeWarper *effectWarper=nullptr, bool clearByTrimming=false)
void RemoveInterval(const IntervalHolder &interval)
std::shared_ptr< const Interval > IntervalConstHolder
sampleCount GetVisibleSampleCount() const
Track::Holder Copy(double t0, double t1, bool forClipboard=true) const override
Create new tracks and don't modify this track.
IntervalHolder GetClip(size_t iInterval)
void HandleClear(double t0, double t1, bool addCutLines, bool split, bool clearByTrimming=false)
WaveClipHolder DoCreateClip(double offset=.0, const wxString &name=wxEmptyString) const
Create a new clip that can be inserted later into the track.
wxString MakeClipCopyName(const wxString &originalName) const
IntervalHolder GetLeftmostClip()
IntervalHolder GetIntervalAtTime(double t)
virtual size_t NChannels() const =0
Report the number of channels.
sampleCount TimeToLongSamples(double t0) const
double SnapToSample(double t) const
static const TypeInfo & ClassTypeInfo()
static XMLMethodRegistry & Get()
Get the unique instance.
void CallWriters(const Host &host, XMLWriter &writer)
This class is an interface which should be implemented by classes which wish to be able to load and s...
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
virtual void StartTag(const wxString &name)
void WriteAttr(const wxString &name, const Identifier &value)
virtual void EndTag(const wxString &name)
Positions or offsets within audio files need a wide type.
ChannelType
Mutually exclusive channel classifications.
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
template struct REGISTRIES_API Cloneable<>
PROJECT_RATE_API sampleFormat SampleFormatChoice()
WAVE_TRACK_API ClipPointers SortedClipArray(WaveChannel &channel)
Get clips sorted by play start time.
std::vector< std::vector< float > > Duplicate(const std::vector< float > &audio, size_t numChannels)
double GetRate(const Track &track)
bool AreAligned(const WaveTrack::IntervalConstHolders &a, const WaveTrack::IntervalConstHolders &b)
bool ClipsAreUnique(const WaveClipHolders &clips)
static const ChannelGroup::Attachments::RegisteredFactory waveTrackDataFactory
WaveTrack::IntervalHolder GetRenderedCopy(const WaveTrack::IntervalHolder &pInterval, const std::function< void(double)> &reportProgress, const SampleBlockFactoryPtr &factory, sampleFormat format)
Track::LinkType ToLinkType(int value)
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept
void copy(const T *src, T *dst, int32_t n)
float *const * Get() const
A convenient base class defining abstract virtual Clone() for a given kind of pointer.
"finally" as in The C++ Programming Language, 4th ed., p. 358 Useful for defining ad-hoc RAII actions...
A convenience for use with range-for.
@ Deserialized
being read from project file
@ New
newly created and empty
@ Inserted
(partly) copied from another clip, or moved from a track
WaveTrack::IntervalHolder right
std::optional< wxString > leftClipName
WaveTrack::IntervalHolder left
std::optional< wxString > rightClipName
~WaveTrackData() override
WaveTrackData & operator=(const WaveTrackData &)=delete
WaveTrackData(const WaveTrackData &)