43#include <unordered_set>
78 const std::function<
void(
double)>& reportProgress,
81 auto &interval = *pInterval;
83 if (!interval.HasPitchOrSpeed())
86 const auto dst = std::make_shared<Interval>(
89 const auto originalPlayStartTime = interval.GetPlayStartTime();
90 const auto originalPlayEndTime = interval.GetPlayEndTime();
91 const auto stretchRatio = interval.GetStretchRatio();
97 interval.TrimLeftTo(originalPlayStartTime);
98 interval.TrimRightTo(originalPlayEndTime);
105 const auto tmpPlayStartTime =
106 std::max(interval.GetSequenceStartTime(), originalPlayStartTime - stretchRatio);
107 const auto tmpPlayEndTime =
108 std::min(interval.GetSequenceEndTime(), originalPlayEndTime + stretchRatio);
109 interval.TrimLeftTo(tmpPlayStartTime);
110 interval.TrimRightTo(tmpPlayEndTime);
112 constexpr auto sourceDurationToDiscard = 0.;
113 constexpr auto blockSize = 1024;
114 const auto numChannels = interval.NChannels();
118 params.timeRatio = stretchRatio;
119 params.pitchRatio = std::pow(2., interval.GetCentShift() / 1200.);
123 stretcherSource, std::move(
params) };
126 const auto totalNumOutSamples =
133 while (numOutSamples < totalNumOutSamples)
135 const auto numSamplesToGet =
137 stretcher.GetSamples(container.
Get(), numSamplesToGet);
140 if (interval.NChannels() == 2)
143 numOutSamples += numSamplesToGet;
146 numOutSamples.as_double() / totalNumOutSamples.as_double());
152 dst->SetPlayStartTime(tmpPlayStartTime);
153 dst->ClearLeft(originalPlayStartTime);
154 dst->ClearRight(originalPlayEndTime);
157 auto dstEnvelope = std::make_unique<Envelope>(interval.GetEnvelope());
158 const auto samplePeriod = 1. / interval.GetRate();
159 dstEnvelope->CollapseRegion(
160 originalPlayEndTime, interval.GetSequenceEndTime() + samplePeriod, samplePeriod);
161 dstEnvelope->CollapseRegion(0, originalPlayStartTime, samplePeriod);
162 dstEnvelope->SetOffset(originalPlayStartTime);
163 dst->SetEnvelope(move(dstEnvelope));
167 assert(!dst->HasPitchOrSpeed());
172std::shared_ptr<const WaveTrack::Interval>
175 std::shared_ptr<const Interval> result;
177 ? std::numeric_limits<double>::max()
178 : std::numeric_limits<double>::lowest();
182 (other->Start() > interval.
Start() && other->Start() < bestMatchTime))
185 (other->Start() < interval.
Start() && other->Start() > bestMatchTime)))
188 bestMatchTime = other->Start();
197 return std::const_pointer_cast<Interval>(
198 std::as_const(*this).GetNextInterval(interval, searchDirection));
205 if (interval->WithinPlayRegion(t))
221 double GetOrigin()
const;
222 void SetOrigin(
double origin);
227 float GetVolume()
const;
228 void SetVolume(
float value);
229 float GetPan()
const;
230 void SetPan(
float value);
233 void SetRate(
int value);
237 std::atomic<float> mGain{ 1.0f };
239 std::atomic<float> mPan{ 0.0f };
242 double mOrigin{ 0.0 };
248 [](
auto &) {
return std::make_unique<WaveTrackData>(); } };
251WaveTrackData::WaveTrackData(
const WaveTrackData &other) {
252 SetVolume(other.GetVolume());
253 SetPan(other.GetPan());
255 mOrigin = other.mOrigin;
256 mFormat = other.mFormat;
259WaveTrackData::~WaveTrackData() =
default;
262 return std::make_unique<WaveTrackData>(*
this);
274double WaveTrackData::GetOrigin()
const
278void WaveTrackData::SetOrigin(
double origin)
293float WaveTrackData::GetVolume()
const
295 return mGain.load(std::memory_order_relaxed);
298void WaveTrackData::SetVolume(
float value)
300 mGain.store(value, std::memory_order_relaxed);
303float WaveTrackData::GetPan()
const
305 return mPan.load(std::memory_order_relaxed);
308void WaveTrackData::SetPan(
float value)
310 mPan.store(value, std::memory_order_relaxed);
318void WaveTrackData::SetRate(
int value)
327 if (a.size() != b.size())
330 const auto compare = [](
const auto &a,
const auto &b) {
334 return a->GetPlayStartTime() == b->GetPlayStartTime() &&
335 a->GetSequenceStartTime() == b->GetSequenceStartTime() &&
336 a->GetPlayEndTime() == b->GetPlayEndTime() &&
337 a->GetSequenceEndTime() == b->GetSequenceEndTime();
340 return std::mismatch(a.begin(), a.end(), b.begin(), compare).first == a.end();
399 auto result = std::make_shared<WaveTrack>(
404 result->CreateRight();
407 result->AttachedTrackObjects::BuildAll();
418 assert(nChannels > 0);
419 assert(nChannels <= 2);
434 assert(pLeft && pRight);
435 const auto pLeftAttachments =
437 const auto pRightAttachments =
440 assert((pLeftAttachments ==
nullptr) == (pRightAttachments ==
nullptr));
441 if (pLeftAttachments) {
443 pRightAttachments->Reparent(shared_from_this());
445 pLeftAttachments->MakeStereo(shared_from_this(),
446 std::move(*pRightAttachments));
456 if (
const auto pAttachments =
458 pAttachments->Erase(shared_from_this(), ii);
464 assert(nChannels > 0);
465 assert(nChannels <= 2);
490 auto result =
tracks.Add(trackFactory.Create());
496 : mpFactory(pFactory)
512 result->AttachedTrackObjects::BuildAll();
519 for (
const auto &clip : orig)
521 std::make_shared<WaveClip>(*clip, pFactory,
true),
522 false, backup,
false);
567 for (
const auto &pInterval :
Intervals())
569 pInterval->ShiftBy(delta);
576 for (
const auto &pInterval :
Intervals())
578 if(pInterval->Start() >= t0)
579 pInterval->ShiftBy(delta);
584 const auto offset = t0 >= 0 ? delta : t0 + delta;
593 return std::static_pointer_cast<WaveTrack>(srcCopy);
603 for (
auto it = clips.begin(); it != clips.end();)
605 if ((*it)->IsEmpty())
606 it = clips.erase(it);
617 if (next ==
nullptr) {
620 wxLogWarning(L
"Right track %s is expected to be a WaveTrack."
621 "\n Removing link from left wave track %s.",
642 removeZeroClips(next->NarrowClips());
656 if (next && next->mLegacyRate > 0)
658 next->SetRate(next->mLegacyRate);
659 next->mLegacyRate = 0;
679 {
"wave",
"wave",
XO(
"Wave Track") },
698 auto &pSampleBlockFactory = trackFactory.GetSampleBlockFactory();
699 auto pFirstTrack =
EmptyCopy(pSampleBlockFactory);
700 list.
Add(pFirstTrack->SharedPointer());
701 pFirstTrack->Paste(0.0, *
this);
702 return pFirstTrack->SharedPointer();
712 return std::static_pointer_cast<Interval>(DoGetInterval(iInterval));
720std::shared_ptr<WideChannelGroupInterval>
731 return std::any_of(clips.begin(), clips.end(),
732 [&](
const auto &pClip){ return pClip->GetName() == name; });
745 return { shared_from_this(), &aliased };
753std::shared_ptr<WaveClipChannel>
755 ::Channel::GetInterval<WaveClipChannel>(iInterval); }
757std::shared_ptr<const WaveClipChannel>
759 ::Channel::GetInterval<const WaveClipChannel>(iInterval); }
766 return ::Channel::Intervals<const WaveClipChannel>(); }
788 newTrack->CopyClips(newTrack->mClips,
789 newTrack->mpFactory, this->mClips, backup);
795 auto name = originalName;
796 for (
auto i = 1;; ++i)
801 name =
XC(
"%s.%i",
"clip name template").Format(originalName, i).Translation();
807 for (
auto i = 1;; ++i)
810 auto name =
XC(
"%s.%i",
"clip name template").Format(
GetName(), i).Translation();
829 newRate = std::max( 1.0, newRate );
833 clip->SetRate(newRate);
839 data.SetRate(
static_cast<int>(newRate));
874 else if (newPan < -1.0)
877 if (
GetPan() != newPan ) {
895 const auto pan =
GetPan();
902 if ((channel % 2) == 0)
903 return left * volume;
905 return right * volume;
913 result += clip->GetVisibleSampleCount();
925 const std::function<
void(
size_t)> & progressReport)
928 pClip->ConvertToSampleFormat(
format, progressReport);
941 if (clip->IntersectsPlayRegion(t0, t1)) {
960 auto result =
Copy(t0, t1);
972 auto result = Copy(t0, t1);
974 return std::static_pointer_cast<WaveTrack>(result);
982 bool inside0 =
false;
983 bool inside1 =
false;
986 if (t1 > clip->GetPlayStartTime() && t1 < clip->GetPlayEndTime()) {
988 clip->GetTrimRight() + clip->GetPlayEndTime() - t1);
992 if (t0 > clip->GetPlayStartTime() && t0 < clip->GetPlayEndTime()) {
994 clip->GetTrimLeft() + t0 - clip->GetPlayStartTime());
1002 ; !inside1 && t1 < endTime
1007 ; !inside0 && t0 > startTime
1016 auto result = std::make_shared<WaveTrack>(
CreateToken{},
1019 result->CreateRight();
1020 result->Init(*
this);
1027 result->DoSetRate(rate);
1028 result->mpFactory = pFactory ? pFactory :
mpFactory;
1036 return EmptyCopy(NChannels(), pFactory);
1042 for (
auto &pClip :
mClips)
1043 pClip->DiscardRightChannel();
1049 assert(!GetOwner());
1057 result->Add(newTrack);
1061 return std::static_pointer_cast<WaveTrack>(result->DetachFirst());
1066 std::vector<Holder> result{ SharedPointer<WaveTrack>() };
1067 if (NChannels() == 2) {
1068 auto pOwner = GetOwner();
1070 auto pNewTrack = result.emplace_back(EmptyCopy(1));
1071 for (
auto &pClip : mClips)
1072 pNewTrack->mClips.emplace_back(pClip->SplitChannels());
1073 this->mRightChannel.reset();
1075 auto iter = pOwner->Find(
this);
1076 pOwner->Insert(*++iter, pNewTrack);
1078 result[0]->EraseChannelAttachments(1);
1079 result[1]->EraseChannelAttachments(0);
1087 for (
const auto &pClip:
mClips)
1088 pClip->SwapChannels();
1090 if (
const auto pAttachments =
1092 pAttachments->SwapChannels(shared_from_this());
1107 if (pClip->IsEmpty())
1109 else if (t0 <= pClip->GetPlayStartTime() && t1 >= pClip->GetPlayEndTime())
1111 newTrack->CopyWholeClip(*pClip, t0, forClipboard);
1113 else if (pClip->CountSamples(t0, t1) >= 1) {
1114 newTrack->CopyPartOfClip(*pClip, t0, t1, forClipboard);
1117 newTrack->FinishCopy(t0, t1, forClipboard);
1122 double t0,
bool forClipboard)
1125 const auto newClip =
1126 std::make_shared<Interval>(clip, pFactory, !forClipboard);
1128 newClip->ShiftBy(-t0);
1132 double t0,
double t1,
bool forClipboard)
1135 auto newClip = std::make_shared<Interval>(
1136 clip, pFactory, !forClipboard, t0, t1);
1137 newClip->SetName(clip.GetName());
1138 newClip->ShiftBy(-t0);
1139 if (newClip->GetPlayStartTime() < 0)
1140 newClip->SetPlayStartTime(0);
1145 double t0,
double t1,
bool forClipboard)
1153 placeholder->SetIsPlaceholder(
true);
1154 placeholder->InsertSilence(0, (t1 - t0) -
GetEndTime());
1226 bool clearByTrimming)
1231 if (!tempo.has_value())
1235 t0, t1, *copyHolder, preserve, merge, effectWarper, clearByTrimming);
1239 double t0,
double t1,
const WaveTrack& src,
bool preserve,
bool merge,
1240 const TimeWarper* effectWarper,
bool clearByTrimming)
1242 const auto srcNChannels = src.
NChannels();
1252 double dur =
std::min(t1 - t0, endTime);
1261 auto &track = *
this;
1263 std::vector<SplitInfo> splits;
1268 auto get_split = [&](
double time) {
1269 auto it = std::find_if(splits.begin(), splits.end(),
1270 [time](
const SplitInfo& split) { return split.time == time; });
1271 if(it == splits.end())
1274 { time, nullptr, nullptr, std::nullopt, std::nullopt }
1281 const TimeWarper *warper = (effectWarper ? effectWarper : &localWarper);
1283 const auto roundTime = [&track](
double t){
1284 return track.SnapToSample(t);
1294 for (
const auto &&clip : track.Intervals()) {
1300 st = roundTime(clip->GetPlayStartTime());
1301 if (st >= t0 && st <= t1) {
1302 auto it = get_split(st);
1303 if (clip->GetTrimLeft() != 0) {
1305 it->right = track.CopyClip(*clip,
false);
1306 it->right->SetTrimLeft(.0);
1307 it->right->ClearRight(clip->GetPlayStartTime());
1309 it->rightClipName = clip->GetName();
1312 st = roundTime(clip->GetPlayEndTime());
1313 if (st >= t0 && st <= t1) {
1314 auto it = get_split(st);
1315 if (clip->GetTrimRight() != 0) {
1317 it->left = track.CopyClip(*clip,
false);
1318 it->left->SetTrimRight(.0);
1319 it->left->ClearLeft(clip->GetPlayEndTime());
1321 it->leftClipName = clip->GetName();
1325 auto cutlines = clip->GetCutLines();
1326 for (
auto &cut : cutlines) {
1327 const auto unrounded =
1328 clip->GetSequenceStartTime() + cut->GetSequenceStartTime();
1329 const double cs = roundTime(unrounded);
1332 if (cs >= t0 && cs <= t1) {
1334 cut->SetSequenceStartTime(cs);
1335 bool removed = clip->RemoveCutLine(unrounded);
1337 cuts.push_back(move(cut));
1342 const auto tolerance = 2.0 / track.GetRate();
1345 constexpr auto split =
false;
1348 track.HandleClear(t0, t1,
false, split, clearByTrimming);
1351 track.PasteWaveTrackAtSameTempo(t0, src, merge);
1354 if (merge && splits.size() > 0) {
1360 auto clips = track.SortedIntervalArray();
1365 for (
const auto clip : clips) {
1368 if (fabs(t1 - clip->GetPlayStartTime()) < tolerance) {
1369 if (prev && clip->HasEqualPitchAndSpeed(*prev))
1371 track.GetClipIndex(*prev), track.GetClipIndex(*clip));
1380 auto clips = track.SortedIntervalArray();
1385 for (
const auto clip : clips) {
1390 if (clip->HasEqualPitchAndSpeed(*prev))
1392 track.GetClipIndex(*prev), track.GetClipIndex(*clip));
1395 if (fabs(t0 - clip->GetPlayEndTime()) < tolerance)
1411 assert(target.GetTrimLeft() == 0);
1412 if (target.GetTrimLeft() != 0)
1418 assert(target.HasEqualPitchAndSpeed(src));
1420 auto trim = src.GetPlayEndTime() - src.GetPlayStartTime();
1421 auto success = target.Paste(target.GetPlayStartTime(), src);
1423 target.SetTrimLeft(trim);
1426 target.ShiftBy(-trim);
1432 assert(target.GetTrimRight() == 0);
1433 if (target.GetTrimRight() != 0)
1436 assert(target.HasEqualPitchAndSpeed(src));
1438 auto trim = src.GetPlayEndTime() - src.GetPlayStartTime();
1439 auto success = target.Paste(target.GetPlayEndTime(), src);
1441 target.SetTrimRight(trim);
1445 for (
const auto& split: splits) {
1446 auto at = roundTime(warper->
Warp(split.time));
1447 for (
const auto &&clip : track.Intervals()) {
1451 if (clip->SplitsPlayRegion(at))
1453 auto newClip =
CopyClip(*clip,
true);
1454 clip->ClearRight(at);
1455 newClip->ClearLeft(at);
1458 attachRight(*clip, *split.left);
1461 attachLeft(*newClip, *split.right);
1462 track.InsertInterval(move(newClip),
false);
1465 else if (clip->GetPlayStartSample() ==
1466 track.TimeToLongSamples(at) && split.right) {
1468 const auto trim = clip->GetTrimLeft();
1469 const auto seqStartTime = clip->GetSequenceStartTime();
1470 clip->Clear(seqStartTime, seqStartTime + trim);
1473 clip->ShiftBy(trim);
1474 attachLeft(*clip, *split.right);
1477 else if (clip->GetPlayEndSample() ==
1478 track.TimeToLongSamples(at) && split.left) {
1481 clip->GetPlayEndTime(), clip->GetSequenceEndTime());
1482 attachRight(*clip, *split.left);
1489 for (
const auto& split : splits)
1491 auto s = track.TimeToLongSamples(warper->
Warp(split.time));
1492 for (
auto &&clip : track.Intervals()) {
1493 if (split.rightClipName.has_value() && clip->GetPlayStartSample() == s)
1494 clip->SetName(*split.rightClipName);
1495 else if (split.leftClipName.has_value() && clip->GetPlayEndSample() == s)
1496 clip->SetName(*split.leftClipName);
1501 for (
const auto &&clip : track.Intervals()) {
1502 const double st = clip->GetPlayStartTime();
1503 const double et = clip->GetPlayEndTime();
1506 for (
auto &cut : cuts) {
1511 double cs = cut->GetSequenceStartTime();
1515 if (cs >= st && cs <= et) {
1516 cut->SetSequenceStartTime(warper->
Warp(cs) - st);
1517 clip->AddCutLine(cut);
1528 constexpr bool addCutLines =
false;
1529 constexpr bool split =
true;
1536 const auto begin = clips.begin();
1537 const auto pred = [&](
auto pClip){
return pClip.get() == &clip; };
1538 auto iter = std::find_if(
begin, clips.end(), pred);
1539 return std::distance(
begin, iter);
1545 if (distance < clips.size())
1546 clips.erase(clips.begin() + distance);
1551 const bool split,
const bool clearByTrimming)
1555 wxASSERT( t1 >= t0 );
1569 if (clip->PartlyWithinPlayRegion(t0, t1)) {
1570 addCutLines =
false;
1575 if (clip->CoversEntirePlayRegion(t0, t1))
1577 clipsToDelete.push_back(clip);
1578 else if (clip->IntersectsPlayRegion(t0, t1)) {
1583 clipsToDelete.push_back(clip);
1584 auto newClip =
CopyClip(*clip,
true);
1585 newClip->ClearAndAddCutLine(t0, t1);
1586 clipsToAdd.push_back(move(newClip));
1589 if (split || clearByTrimming) {
1592 if (clip->BeforePlayRegion(t0)) {
1597 clipsToDelete.push_back(clip);
1598 auto newClip =
CopyClip(*clip,
true);
1599 newClip->TrimLeft(t1 - clip->GetPlayStartTime());
1603 newClip->ShiftBy(t0 - t1);
1604 clipsToAdd.push_back(move(newClip));
1606 else if (clip->AfterPlayRegion(t1)) {
1611 clipsToDelete.push_back(clip);
1612 auto newClip =
CopyClip(*clip,
true);
1613 newClip->TrimRight(clip->GetPlayEndTime() - t0);
1615 clipsToAdd.push_back(move(newClip));
1621 auto leftClip =
CopyClip(*clip,
true);
1622 leftClip->TrimRight(clip->GetPlayEndTime() - t0);
1623 clipsToAdd.push_back(move(leftClip));
1625 auto rightClip =
CopyClip(*clip,
true);
1626 rightClip->TrimLeft(t1 - clip->GetPlayStartTime());
1630 rightClip->ShiftBy(t0 - t1);
1631 clipsToAdd.push_back(move(rightClip));
1633 clipsToDelete.push_back(clip);
1641 clipsToDelete.push_back(clip);
1642 auto newClip =
CopyClip(*clip,
true);
1645 newClip->Clear(t0,t1);
1647 clipsToAdd.push_back(move(newClip));
1656 for (
const auto &clip: clipsToDelete)
1664 if (clip->AtOrBeforePlayRegion(t1))
1665 clip->ShiftBy(-(t1 - t0));
1667 for (
auto &clip: clipsToAdd)
1674 if (newT1 > oldT1 &&
1680 if (newT1 > oldT1) {
1687 const auto offset = newT1 - oldT1;
1690 if (clip->GetPlayStartTime() > oldT1 - (1.0 / rate))
1691 clip->ShiftBy(offset);
1698 const auto duration = newT1 - oldT1;
1700 tmp->InsertSilence(0.0, duration);
1705 else if (newT1 < oldT1)
1706 Clear(newT1, oldT1);
1714 if (!tempo.has_value())
1721 double t0,
const WaveTrack& other,
bool merge)
1723 const auto otherNChannels = other.
NChannels();
1731 const auto insertDuration = endTime;
1732 auto &track = *
this;
1757 t0 = track.SnapToSample(t0);
1761 const auto clipAtT0 = track.GetIntervalAtTime(t0);
1764 const auto pitchAndSpeedMatch =
1765 !clipAtT0 || (clipAtT0->HasEqualPitchAndSpeed(*otherFirstClip) &&
1766 clipAtT0->HasEqualPitchAndSpeed(*otherLastClip));
1770 const bool singleClipMode =
1772 std::abs(startTime) < track.LongSamplesToTime(1) * 0.5 &&
1773 pitchAndSpeedMatch && merge;
1775 const auto rate = track.GetRate();
1776 if (insertDuration != 0 && insertDuration < 1.0 / rate)
1789 XO(
"There is not enough room available to paste the selection"),
1790 XO(
"Warning"),
"Error:_Insufficient_space_in_track"
1794 if (editClipCanMove) {
1795 if (!singleClipMode)
1802 for (
const auto& clip : track.Intervals())
1803 if (clip->GetPlayStartTime() > t0 - (1.0 / rate))
1804 clip->ShiftBy(insertDuration);
1810 const auto clipAtT0 = track.GetClipAtTime(t0);
1811 const auto t = clipAtT0 ? clipAtT0->GetPlayEndTime() : t0;
1812 if (!track.IsEmpty(t, t + insertDuration))
1813 throw notEnoughSpaceException;
1820 if (singleClipMode && merge) {
1825 for (
const auto& clip : track.Intervals()) {
1826 if (editClipCanMove) {
1827 if (clip->SplitsPlayRegion(t0)) {
1836 if (clip->WithinPlayRegion(t0))
1847 if (!editClipCanMove) {
1850 for (
const auto& clip : track.Intervals()) {
1851 if (clip->GetPlayStartTime() > insideClip->GetPlayStartTime() &&
1852 insideClip->GetPlayEndTime() + insertDuration >
1853 clip->GetPlayStartTime())
1856 throw notEnoughSpaceException;
1859 if (
auto pClip = other.
GetClip(0)) {
1863 assert(insideClip->GetStretchRatio() == pClip->GetStretchRatio());
1866 assert(insideClip->NChannels() == pClip->NChannels());
1867 bool success = insideClip->Paste(t0, *pClip);
1878 if (!editClipCanMove &&
1879 !track.IsEmpty(t0, t0 + insertDuration - 1.0 / rate))
1882 throw notEnoughSpaceException;
1884 for (
const auto& clip : other.
Intervals()) {
1886 if (!clip->GetIsPlaceholder()) {
1888 const auto name = clip->GetName().IsEmpty()
1889 ? track.MakeNewClipName()
1891 const auto oldPlayStart = clip->GetPlayStartTime();
1892 const auto newSequenceStart =
1893 (oldPlayStart + t0) - clip->GetTrimLeft();
1894 const auto newClip =
CreateClip(newSequenceStart,
name, clip.get());
1895 newClip->Resample(rate);
1896 track.InsertInterval(move(newClip),
false);
1904 std::optional<double> oRate;
1906 return std::all_of(channels.begin(), channels.end(),
1911 const auto rate = pTrack->mLegacyRate;
1914 else if (*oRate != rate)
1923 return std::all_of(channels.begin(), channels.end(),
1925 return pTrack && pTrack->mLegacyFormat == mLegacyFormat;
1930 bool newClip,
bool backup,
bool allowEmpty)
1932 if (!backup && !clip->GetIsPlaceholder() && !allowEmpty && clip->IsEmpty())
1936 if (tempo.has_value())
1937 clip->OnProjectTempoChange(std::nullopt, *tempo);
1938 clips.push_back(std::move(clip));
1950 assert(!interval.has_value() ||
1951 interval->first <= interval->second);
1954 const auto startTime =
1957 const auto endTime =
1960 if (startTime >= endTime)
1965 clipAtT0 && clipAtT0->SplitsPlayRegion(startTime) &&
1966 clipAtT0->HasPitchOrSpeed())
1967 Split(startTime, startTime);
1969 clipAtT1 && clipAtT1->SplitsPlayRegion(endTime) &&
1970 clipAtT1->HasPitchOrSpeed())
1971 Split(endTime, endTime);
1975 while (clip && clip->GetPlayStartTime() < endTime)
1977 if (clip->HasPitchOrSpeed())
1978 srcIntervals.push_back(clip);
1988 if (
const auto other =
dynamic_cast<const WaveTrack*
>(&src))
1992 constexpr auto merge =
true;
2011 auto clipStart = pClip->GetPlayStartSample();
2012 auto clipEnd = pClip->GetPlayEndSample();
2013 if (clipEnd > start && clipStart <
end) {
2014 auto offset = std::max(start - clipStart,
sampleCount(0));
2016 auto length =
std::min(
end, clipEnd) - (clipStart + offset);
2017 pClip->SetSilence(offset, length);
2033 if (clips.empty()) {
2036 clip->InsertSilence(0, len);
2043 const auto end = clips.end();
2044 const auto it = std::find_if(clips.begin(),
end,
2045 [&](
const IntervalHolder &clip) { return clip->SplitsPlayRegion(t); } );
2049 (*it)->InsertSilence(t, len);
2052 for (
const auto &&clip : clips)
2053 if (clip->BeforePlayRegion(t))
2064 const size_t maxAtOnce = 1048576;
2065 std::vector<float> buffer;
2066 std::vector<samplePtr> buffers;
2071 for (
const auto &interval :
Intervals()) {
2072 double startTime = interval->Start();
2073 double endTime = interval->End();
2075 if (endTime < t0 || startTime > t1)
2079 if (buffer.empty()) {
2080 buffer.resize(maxAtOnce * width);
2081 buffers.resize(width);
2082 auto pBuffer = buffer.data();
2083 for (
size_t ii = 0; ii < width; ++ii, pBuffer += maxAtOnce)
2084 buffers[ii] =
reinterpret_cast<samplePtr>(pBuffer);
2087 const auto allZeroesAt = [&](
size_t i) {
2088 auto pData = buffer.data() + i;
2089 for (
size_t ii = 0; ii < width; ++ii, pData += maxAtOnce) {
2100 auto start = interval->TimeToSamples(std::max(.0, t0 - startTime));
2101 auto end = interval->TimeToSamples(
std::min(endTime, t1) - startTime);
2103 auto len = (
end - start);
2104 for (
decltype(len) done = 0; done < len; done += maxAtOnce) {
2107 auto bufferIt = buffers.begin();
2109 for (
auto channel : interval->Channels())
2110 channel->GetSamples(
2111 *bufferIt++,
floatSample, start + done, numSamples);
2113 for (
decltype(numSamples) i = 0; i < numSamples; ++i) {
2114 auto curSamplePos = start + done + i;
2117 if (seqStart == -1 && allZeroesAt(i))
2118 seqStart = curSamplePos;
2119 else if (curSamplePos ==
end - 1 || !allZeroesAt(i)) {
2120 if (seqStart != -1) {
2121 decltype(
end) seqEnd;
2124 if (curSamplePos ==
end - 1 && allZeroesAt(i))
2127 seqEnd = curSamplePos;
2128 if (seqEnd - seqStart + 1 > minSamples) {
2131 startTime + interval->SamplesToTime(seqStart),
2132 startTime + interval->SamplesToTime(seqEnd)
2143 for (
const auto ®ion : regions)
2156 for (
const auto &interval : intervals)
2157 if (interval->IntersectsPlayRegion(t0, t1))
2158 intervalsToJoin.push_back(interval);
2159 if (intervalsToJoin.size() < 2u)
2162 intervalsToJoin.begin() + 1, intervalsToJoin.end(),
2163 [first = intervalsToJoin[0]](
const auto& interval) {
2164 return !first->HasEqualPitchAndSpeed(*interval);
2173 for (
const auto &clip: intervals) {
2174 if (clip->IntersectsPlayRegion(t0, t1)) {
2176 auto it = clipsToDelete.begin(),
end = clipsToDelete.end();
2177 for (; it !=
end; ++it)
2178 if ((*it)->GetPlayStartTime() > clip->GetPlayStartTime())
2181 clipsToDelete.insert(it, clip);
2186 if (clipsToDelete.empty())
2189 const auto firstToDelete = clipsToDelete[0].get();
2190 auto t = firstToDelete->GetPlayStartTime();
2193 firstToDelete->GetSequenceStartTime(),
2194 firstToDelete->GetName());
2196 for (
const auto &clip : clipsToDelete) {
2201 if (clip->GetPlayStartTime() - t > (1.0 / rate))
2203 double addedSilence = (clip->GetPlayStartTime() - t);
2205 auto offset = clip->GetPlayStartTime();
2206 auto value = clip->GetEnvelope().GetValue(offset);
2207 newClip->AppendSilence(addedSilence, value);
2212 bool success = newClip->Paste(t, *clip);
2215 t = newClip->GetPlayEndTime();
2227 size_t len,
unsigned stride,
sampleFormat effectiveFormat)
2250 size_t len,
unsigned int stride,
sampleFormat effectiveFormat)
2257 buffers,
format, len, stride, effectiveFormat);
2265 auto startSample = clip->GetPlayStartSample();
2266 auto endSample = clip->GetPlayEndSample();
2267 if (s >= startSample && s < endSample)
2271 clip->GetBestBlockSize(s - clip->GetSequenceStartSample());
2276 return bestBlockSize;
2282 auto maxblocksize = std::accumulate(clips.begin(), clips.end(),
size_t{},
2283 [](
size_t acc,
auto pClip){
2284 return std::max(acc, pClip->GetMaxBlockSize()); });
2286 if (maxblocksize == 0)
2295 wxASSERT(maxblocksize > 0);
2297 return maxblocksize;
2304 .GetSequence(0)->GetIdealBlockSize();
2326 pInterval->RepairChannels();
2367 for (
const auto& pair : attrs)
2369 const auto& attr = pair.first;
2370 const auto& value = pair.second;
2375 if (!value.TryGet(dblValue) ||
2376 (dblValue < 1.0) || (dblValue > 1000000.0))
2382 else if (attr ==
Offset_attr && value.TryGet(dblValue))
2393 else if (attr ==
Volume_attr && value.TryGet(dblValue))
2395 else if (attr ==
Pan_attr && value.TryGet(dblValue) &&
2396 (dblValue >= -1.0) && (dblValue <= 1.0))
2398 else if (attr ==
Linked_attr && value.TryGet(nValue))
2431 const auto getClip = [
this]() ->
WaveClip & {
2443 return getClip().GetSequence(0);
2444 else if (tag ==
"envelope")
2445 return &getClip().GetEnvelope();
2453 auto pSeq = getClip().GetSequence(0);
2463 auto clip = std::make_shared<WaveClip>(1,
2465 const auto xmlHandler = clip.get();
2467 clips.push_back(std::move(clip));
2478 const auto channels = Channels();
2480 nChannels = channels.size();
2481 for (
const auto pChannel : channels)
2482 WriteOneXML(*pChannel, xmlFile,
iChannel++, nChannels);
2500 track.Track::WriteCommonXMLAttributes(xmlFile);
2510 const auto channelType = (nChannels == 0)
2520 const auto linkType =
static_cast<int>(
2521 (
iChannel == 0) && (nChannels == 2)
2528 const auto useLegacy = track.mLegacyRate != 0;
2531 track.WritableSampleTrack::WriteXMLAttributes(xmlFile);
2542 for (
const auto &clip : channel.
Intervals())
2543 clip->WriteXML(xmlFile);
2551 const auto width = pClip->NChannels();
2552 for (
size_t ii = 0; ii < width; ++ii)
2553 if (pClip->GetSequence(ii)->GetErrorOpening())
2554 return XO(
"A track has a corrupted sample sequence.");
2561 auto clips = Intervals();
2564 const auto begin = clips.begin(),
2565 iter = std::min_element(
begin, clips.end(),
2566 [](
const auto& a,
const auto b) {
2567 return a->GetPlayStartTime() < b->GetPlayStartTime();
2569 return GetClip(std::distance(
begin, iter));
2577 auto clips = Intervals();
2580 const auto begin = clips.begin(),
2581 iter = std::max_element(
begin, clips.end(),
2582 [](
const auto& a,
const auto b) {
2583 return a->GetPlayEndTime() < b->GetPlayEndTime();
2585 return GetClip(std::distance(
begin, iter));
2595 return { clips.begin(), clips.end() };
2621 bool mayThrow,
sampleCount* pNumWithinClips)
const
2626 assert(nBuffers <= 1);
2628 buffers,
format, start, len, backwards, fill, mayThrow, pNumWithinClips);
2639 bool mayThrow,
sampleCount* pNumWithinClips)
const
2642 assert(
iChannel + nBuffers <= nChannels);
2643 return std::all_of(buffers, buffers + nBuffers, [&](
samplePtr buffer) {
2645 buffer,
format, start, len, backwards, fill, mayThrow,
2653 bool backwards,
fillFormat fill,
bool mayThrow,
2661 bool doClear =
true;
2664 for (
const auto &clip: clips)
2666 if (start >= clip->GetPlayStartSample() && start+len <= clip->GetPlayEndSample())
2681 float * pBuffer = (
float*)buffer;
2682 for(
size_t i=0;i<len;i++)
2687 wxFAIL_MSG(
wxT(
"Invalid fill format"));
2692 for (
const auto &clip: clips)
2694 auto clipStart = clip->GetPlayStartSample();
2695 auto clipEnd = clip->GetPlayEndSample();
2697 if (clipEnd > start && clipStart < start+len)
2699 if (clip->HasPitchOrSpeed())
2703 auto samplesToCopy =
2704 std::min( start+len - clipStart, clip->GetVisibleSampleCount() );
2705 auto startDelta = clipStart - start;
2706 decltype(startDelta) inclipDelta = 0;
2709 inclipDelta = -startDelta;
2710 samplesToCopy -= inclipDelta;
2726 startDelta.as_size_t() *
2728 format, inclipDelta, samplesToCopy.as_size_t(), mayThrow ))
2731 samplesCopied += samplesToCopy;
2734 if( pNumWithinClips )
2735 *pNumWithinClips = samplesCopied;
2736 if (result ==
true && backwards)
2745 for (
const auto& channel :
Channels()) {
2746 result.push_back(channel->GetSampleView(t0, t1, mayThrow));
2754 std::vector<std::shared_ptr<const WaveClipChannel>>
2755 intersectingIntervals;
2756 for (
const auto &interval :
Intervals())
2757 if (interval->Intersects(t0, t1))
2758 intersectingIntervals.push_back(interval);
2759 if (intersectingIntervals.empty())
2763 intersectingIntervals.begin(), intersectingIntervals.end(),
2764 [](
const auto& a,
const auto& b) { return a->Start() < b->Start(); });
2765 std::vector<AudioSegmentSampleView> segments;
2766 segments.reserve(2 * intersectingIntervals.size() + 1);
2767 for (
auto i = 0u; i < intersectingIntervals.size();++i)
2769 const auto& interval = intersectingIntervals[i];
2770 const auto intervalStartTime = interval->Start();
2771 if (t0 < intervalStartTime)
2775 t0 = intervalStartTime;
2777 const auto intervalT0 = t0 - intervalStartTime;
2778 const auto intervalT1 =
std::min(t1, interval->End()) - intervalStartTime;
2779 if(intervalT1 > intervalT0)
2782 interval->GetSampleView(intervalT0, intervalT1, mayThrow);
2783 t0 += intervalT1 - intervalT0;
2784 segments.push_back(std::move(newSegment));
2801 auto clipStart = clip->GetPlayStartSample();
2802 auto clipEnd = clip->GetPlayEndSample();
2804 if (clipEnd > start && clipStart < start+len)
2807 if (clip->HasPitchOrSpeed())
2811 auto samplesToCopy =
2812 std::min( start+len - clipStart, clip->GetVisibleSampleCount() );
2813 auto startDelta = clipStart - start;
2814 decltype(startDelta) inclipDelta = 0;
2817 inclipDelta = -startDelta;
2818 samplesToCopy -= inclipDelta;
2834 format, inclipDelta, samplesToCopy.as_size_t(), effectiveFormat );
2849 result = std::max(result, pClip->GetSampleFormats().Effective());
2863 auto clips = pTrack->Intervals();
2864 return std::all_of(clips.begin(), clips.end(),
2865 [](
const auto &pClip){ return pClip->GetEnvelope().IsTrivial(); });
2869 double* buffer,
size_t bufferLen,
double t0,
bool backwards)
const
2875 double* buffer,
size_t bufferLen,
double t0,
bool backwards)
const
2882 t0 -= bufferLen / pTrack->GetRate();
2893 for (
decltype(bufferLen) i = 0; i < bufferLen; i++)
2898 double startTime = t0;
2899 const auto rate = pTrack->GetRate();
2900 auto tstep = 1.0 / rate;
2901 double endTime = t0 + tstep * bufferLen;
2902 for (
const auto &clip: pTrack->Intervals())
2905 auto dClipStartTime = clip->GetPlayStartTime();
2906 auto dClipEndTime = clip->GetPlayEndTime();
2907 if ((dClipStartTime < endTime) && (dClipEndTime > startTime))
2910 auto rlen = bufferLen;
2913 if (rt0 < dClipStartTime)
2917 auto nDiff = (
sampleCount)floor((dClipStartTime - rt0) * rate + 0.5);
2918 auto snDiff = nDiff.as_size_t();
2920 wxASSERT(snDiff <= rlen);
2922 rt0 = dClipStartTime;
2925 if (rt0 + rlen*tstep > dClipEndTime)
2927 auto nClipLen = clip->GetPlayEndSample() - clip->GetPlayStartSample();
2938 rlen =
std::min(rlen,
size_t(floor(0.5 + (dClipEndTime - rt0) / tstep)));
2942 clip->GetEnvelope().GetValues(rbuf, rlen, rt0, tstep);
2954 auto p = std::find_if(
2955 clips.rbegin(), clips.rend(), [&](
const auto &pClip) {
2956 return pClip->WithinPlayRegion(time);
2958 return p != clips.rend() ? *p :
nullptr;
2966 std::make_shared<WaveClip>(*pToCopy, mpFactory, copyCutlines);
2967 pNewClip->SetName(
name);
2968 pNewClip->SetSequenceStartTime(offset);
2972 return DoCreateClip(offset,
name);
2978 return CreateClip(toCopy.GetSequenceStartTime(),
2979 toCopy.GetName(), &toCopy, copyCutlines);
2990 auto clip = std::make_shared<WaveClip>(NChannels(),
2991 mpFactory, GetSampleFormat(),
GetRate());
2992 clip->SetName(
name);
2993 clip->SetSequenceStartTime(offset);
2996 if (tempo.has_value())
2997 clip->OnProjectTempoChange(std::nullopt, *tempo);
2998 assert(clip->NChannels() == NChannels());
3004 const auto &intervals = Intervals();
3005 if (intervals.empty()) {
3007 const auto name = MakeNewClipName();
3008 auto pInterval = CreateClip(origin,
name);
3009 InsertInterval(pInterval,
true,
true);
3013 return mClips.back();
3019 if (mClips.empty()) {
3020 auto pInterval = CreateClip(
3022 InsertInterval(pInterval,
true,
true);
3026 auto end = mClips.end(),
3027 it = max_element(mClips.begin(),
end,
3028 [](
const auto &pClip1,
const auto &pClip2){
3029 return pClip1->GetPlayStartTime() < pClip2->GetPlayStartTime();
3042 [&](
const auto &pOtherClip){
return &clip == pOtherClip.get(); };
3043 auto begin = clips.begin(),
3045 iter = std::find_if(
begin,
end, test);
3046 return std::distance(
begin, iter);
3055 const std::vector<Interval*> &movingClips,
3057 double *allowedAmount )
3060 *allowedAmount = amount;
3062 const auto &moving = [&](
Interval *clip){
3065 return movingClips.end() !=
3066 std::find(movingClips.begin(), movingClips.end(), clip);
3070 if ( moving( c.get() ) )
3072 for (
const auto clip : movingClips) {
3073 if (c->GetPlayStartTime() < clip->GetPlayEndTime() + amount &&
3074 c->GetPlayEndTime() > clip->GetPlayStartTime() + amount)
3081 if (c->GetPlayStartTime() - clip->GetPlayEndTime() < *allowedAmount)
3082 *allowedAmount = c->GetPlayStartTime() - clip->GetPlayEndTime();
3083 if (*allowedAmount < 0)
3087 if (c->GetPlayEndTime() - clip->GetPlayStartTime() > *allowedAmount)
3088 *allowedAmount = c->GetPlayEndTime() - clip->GetPlayStartTime();
3089 if (*allowedAmount > 0)
3098 if (*allowedAmount == amount)
3114 const Interval& candidateClip,
double& slideBy,
double tolerance)
const
3120 const auto candidateClipStartTime = candidateClip.GetPlayStartTime();
3121 const auto candidateClipEndTime = candidateClip.GetPlayEndTime();
3122 const auto t0 =
SnapToSample(candidateClipStartTime + slideBy);
3123 const auto t1 =
SnapToSample(candidateClipEndTime + slideBy);
3124 std::vector<double> overlaps;
3126 clips.begin(), clips.end(), std::back_inserter(overlaps),
3127 [&](
const auto& pClip) {
3128 return pClip->IntersectsPlayRegion(t0, t1) ?
3129 std::min(pClip->GetPlayEndTime(), t1) -
3130 std::max(pClip->GetPlayStartTime(), t0) :
3133 const auto maxOverlap = std::max_element(overlaps.begin(), overlaps.end());
3134 if (*maxOverlap > tolerance)
3136 auto iter = clips.begin();
3137 std::advance(iter, std::distance(overlaps.begin(), maxOverlap));
3138 const auto overlappedClip = *iter;
3139 const auto requiredOffset = slideBy +
3140 *maxOverlap * (overlappedClip->GetPlayStartTime() < t0 ? 1 : -1);
3143 clips.begin(), clips.end(),
3144 [&](
const auto& pClip)
3146 const auto result = pClip->IntersectsPlayRegion(
3147 SnapToSample(candidateClipStartTime + requiredOffset),
3148 SnapToSample(candidateClipEndTime + requiredOffset));
3152 slideBy = requiredOffset;
3167 for (
const auto &&c : Intervals()) {
3168 if (c->SplitsPlayRegion(t)) {
3169 t = SnapToSample(t);
3170 auto newClip = CopyClip(*c,
true);
3172 newClip->TrimLeftTo(t);
3173 auto result = std::pair{ c, newClip };
3177 InsertInterval(move(newClip),
false);
3187 const auto clip1 =
GetClip(clipidx1);
3188 const auto clip2 =
GetClip(clipidx2);
3190 if (!clip1 || !clip2)
3193 if (!clip1->HasEqualPitchAndSpeed(*clip2))
3198 bool success = clip1->Paste(clip1->GetPlayEndTime(), *clip2);
3212 dstIntervals.reserve(srcIntervals.size());
3214 srcIntervals.begin(), srcIntervals.end(),
3215 std::back_inserter(dstIntervals), [&](
const IntervalHolder& interval) {
3216 return GetRenderedCopy(interval,
3217 reportProgress, mpFactory, GetSampleFormat());
3222 for (
auto i = 0; i < srcIntervals.size(); ++i)
3230 using Set = std::unordered_set<WaveClipHolder>;
3231 return clips.size() == Set{ clips.begin(), clips.end() }.size();
3236 bool newClip,
bool allowEmpty)
3239 constexpr bool backup =
false;
3249 iter = find(
mClips.begin(),
end, interval);
3258 assert(oldOne->NChannels() == newOne->NChannels());
3261 newOne->SetName(oldOne->GetName());
3269 pClip->Resample(rate, progress);
3278 for (
const auto &pChannel :
Channels()) {
3279 const auto buffer = buffers[ii++];
3281 result = pChannel->SetFloats(buffer, start, len, effectiveFormat)
3291 const auto comp = [](
const auto &a,
const auto &b) {
3292 return a->GetPlayStartTime() < b->GetPlayStartTime(); };
3293 std::sort(clips.begin(), clips.end(), comp);
3299 const auto &intervals = Intervals();
3301 copy(intervals.begin(), intervals.end(), back_inserter(result));
3302 sort(result.begin(), result.end(), [](
const auto &pA,
const auto &pB){
3303 return pA->GetPlayStartTime() < pB->GetPlayStartTime(); });
3311 copy(intervals.begin(), intervals.end(), back_inserter(result));
3312 sort(result.begin(), result.end(), [](
const auto &pA,
const auto &pB){
3313 return pA->GetPlayStartTime() < pB->GetPlayStartTime(); });
3326 auto iter = pOwner->Find(
this);
3327 assert(
this == *iter);
3329 assert(iter != pOwner->end());
3330 auto pRight =
dynamic_cast<WaveTrack*
>(*iter);
3331 assert(pRight && pRight->NChannels() == 1);
3344 auto iterMe =
mClips.begin(),
3346 auto iterRight = pRight->mClips.begin(),
3347 endRight = pRight->mClips.end();
3348 while (iterMe != endMe && iterRight != endRight) {
3349 (*iterMe)->MakeStereo(std::move(**iterRight), mustAlign);
3353 assert(!mustAlign || (iterMe == endMe && iterRight == endRight));
3355 while (iterRight != endRight) {
3357 mClips.emplace_back(move(*iterRight));
3363 pOwner->Remove(*pRight);
3367 return std::make_shared< WaveTrackFactory >(
3399 L
"/GUI/TrackNames/DefaultTrackName",
3413 bool editClipsCanMove;
3418 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
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 constexpr auto Volume_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
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 void AssignUniqueId(const Track::Holder &track)
Assigns a new unique non-persistent id to a track.
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
void GetEnvelopeValues(double *buffer, size_t bufferLen, double t0, bool backwards) const override
IteratorRange< IntervalIterator< WaveClipChannel > > Intervals()
float GetChannelVolume(int channel) const override
Takes volume and pan into account.
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.
float GetChannelVolume(int channel) const override
Takes volume and pan into account.
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
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 Resample(int rate, BasicUI::ProgressDialog *progress=NULL)
void FinishCopy(double t0, double t1, bool forClipboard)
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)
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.
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
void ShiftBy(double t0, double delta) override
static double ProjectNyquistFrequency(const AudacityProject &project)
void MergeChannelAttachments(WaveTrack &&other)
IntervalHolder GetRightmostClip()
bool CanInsertClip(const Interval &clip, double &slideBy, double tolerance) const
void DoSetVolume(float value)
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 SetVolume(float newVolume)
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)
BuiltinEffectsModule::Registration< Reverse > reverse
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 &)