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 GetGain()
const;
228 void SetGain(
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 SetGain(other.GetGain());
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::GetGain()
const
295 return mGain.load(std::memory_order_relaxed);
298void WaveTrackData::SetGain(
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);
577 return std::static_pointer_cast<WaveTrack>(srcCopy);
587 for (
auto it = clips.begin(); it != clips.end();)
589 if ((*it)->IsEmpty())
590 it = clips.erase(it);
601 if (next ==
nullptr) {
604 wxLogWarning(L
"Right track %s is expected to be a WaveTrack."
605 "\n Removing link from left wave track %s.",
626 removeZeroClips(next->NarrowClips());
641 next->mLegacyRate = 0;
660 {
"wave",
"wave",
XO(
"Wave Track") },
679 auto &pSampleBlockFactory = trackFactory.GetSampleBlockFactory();
680 auto pFirstTrack =
EmptyCopy(pSampleBlockFactory);
681 list.
Add(pFirstTrack->SharedPointer());
682 pFirstTrack->Paste(0.0, *
this);
683 return pFirstTrack->SharedPointer();
693 return std::static_pointer_cast<Interval>(DoGetInterval(iInterval));
701std::shared_ptr<WideChannelGroupInterval>
712 return std::any_of(clips.begin(), clips.end(),
713 [&](
const auto &pClip){ return pClip->GetName() == name; });
726 return { shared_from_this(), &aliased };
734std::shared_ptr<WaveClipChannel>
736 ::Channel::GetInterval<WaveClipChannel>(iInterval); }
738std::shared_ptr<const WaveClipChannel>
740 ::Channel::GetInterval<const WaveClipChannel>(iInterval); }
747 return ::Channel::Intervals<const WaveClipChannel>(); }
769 newTrack->CopyClips(newTrack->mClips,
770 newTrack->mpFactory, this->mClips, backup);
776 auto name = originalName;
777 for (
auto i = 1;; ++i)
782 name =
XC(
"%s.%i",
"clip name template").Format(originalName, i).Translation();
789 for (
auto i = 1;; ++i)
794 name =
XC(
"%s %i",
"clip name template").Format(
GetName(), i).Translation();
811 newRate = std::max( 1.0, newRate );
815 clip->SetRate(newRate);
821 data.SetRate(
static_cast<int>(newRate));
856 else if (newPan < -1.0)
859 if (
GetPan() != newPan ) {
877 const auto pan =
GetPan();
884 if ((channel % 2) == 0)
895 result += clip->GetVisibleSampleCount();
907 const std::function<
void(
size_t)> & progressReport)
910 pClip->ConvertToSampleFormat(
format, progressReport);
923 if (clip->IntersectsPlayRegion(t0, t1)) {
942 auto result =
Copy(t0, t1);
954 auto result = Copy(t0, t1);
956 return std::static_pointer_cast<WaveTrack>(result);
964 bool inside0 =
false;
965 bool inside1 =
false;
968 if (t1 > clip->GetPlayStartTime() && t1 < clip->GetPlayEndTime()) {
970 clip->GetTrimRight() + clip->GetPlayEndTime() - t1);
974 if (t0 > clip->GetPlayStartTime() && t0 < clip->GetPlayEndTime()) {
976 clip->GetTrimLeft() + t0 - clip->GetPlayStartTime());
984 ; !inside1 && t1 < endTime
989 ; !inside0 && t0 > startTime
998 auto result = std::make_shared<WaveTrack>(
CreateToken{},
1001 result->CreateRight();
1002 result->Init(*
this);
1009 result->DoSetRate(rate);
1010 result->mpFactory = pFactory ? pFactory :
mpFactory;
1018 return EmptyCopy(NChannels(), pFactory);
1024 for (
auto &pClip :
mClips)
1025 pClip->DiscardRightChannel();
1031 assert(!GetOwner());
1039 result->Add(newTrack);
1043 return std::static_pointer_cast<WaveTrack>(result->DetachFirst());
1048 std::vector<Holder> result{ SharedPointer<WaveTrack>() };
1049 if (NChannels() == 2) {
1050 auto pOwner = GetOwner();
1052 auto pNewTrack = result.emplace_back(EmptyCopy(1));
1053 for (
auto &pClip : mClips)
1054 pNewTrack->mClips.emplace_back(pClip->SplitChannels());
1055 this->mRightChannel.reset();
1056 auto iter = pOwner->Find(
this);
1057 pOwner->Insert(*++iter, pNewTrack);
1059 result[0]->EraseChannelAttachments(1);
1060 result[1]->EraseChannelAttachments(0);
1068 for (
const auto &pClip:
mClips)
1069 pClip->SwapChannels();
1071 if (
const auto pAttachments =
1073 pAttachments->SwapChannels(shared_from_this());
1084 const auto endTime = std::max(
GetEndTime(), t1);
1089 if (pClip->IsEmpty())
1091 else if (t0 <= pClip->GetPlayStartTime() && t1 >= pClip->GetPlayEndTime())
1093 newTrack->CopyWholeClip(*pClip, t0, forClipboard);
1095 else if (pClip->CountSamples(t0, t1) >= 1) {
1096 newTrack->CopyPartOfClip(*pClip, t0, t1, forClipboard);
1099 newTrack->FinishCopy(t0, t1, endTime, forClipboard);
1104 double t0,
bool forClipboard)
1107 const auto newClip =
1108 std::make_shared<Interval>(clip, pFactory, !forClipboard);
1110 newClip->ShiftBy(-t0);
1114 double t0,
double t1,
bool forClipboard)
1117 auto newClip = std::make_shared<Interval>(
1118 clip, pFactory, !forClipboard, t0, t1);
1119 newClip->SetName(clip.GetName());
1120 newClip->ShiftBy(-t0);
1121 if (newClip->GetPlayStartTime() < 0)
1122 newClip->SetPlayStartTime(0);
1127 double t0,
double t1,
double endTime,
bool forClipboard)
1133 if (forClipboard && endTime + 1.0 /
GetRate() < t1 - t0) {
1135 placeholder->SetIsPlaceholder(
true);
1136 placeholder->InsertSilence(0, (t1 - t0) -
GetEndTime());
1208 bool clearByTrimming)
1213 if (!tempo.has_value())
1217 t0, t1, *copyHolder, preserve, merge, effectWarper, clearByTrimming);
1221 double t0,
double t1,
const WaveTrack& src,
bool preserve,
bool merge,
1222 const TimeWarper* effectWarper,
bool clearByTrimming)
1224 const auto srcNChannels = src.
NChannels();
1234 double dur =
std::min(t1 - t0, endTime);
1243 auto &track = *
this;
1245 std::vector<SplitInfo> splits;
1250 auto get_split = [&](
double time) {
1251 auto it = std::find_if(splits.begin(), splits.end(),
1252 [time](
const SplitInfo& split) { return split.time == time; });
1253 if(it == splits.end())
1256 { time, nullptr, nullptr, std::nullopt, std::nullopt }
1263 const TimeWarper *warper = (effectWarper ? effectWarper : &localWarper);
1265 const auto roundTime = [&track](
double t){
1266 return track.SnapToSample(t);
1276 for (
const auto &&clip : track.Intervals()) {
1282 st = roundTime(clip->GetPlayStartTime());
1283 if (st >= t0 && st <= t1) {
1284 auto it = get_split(st);
1285 if (clip->GetTrimLeft() != 0) {
1287 it->right = track.CopyClip(*clip,
false);
1288 it->right->SetTrimLeft(.0);
1289 it->right->ClearRight(clip->GetPlayStartTime());
1291 it->rightClipName = clip->GetName();
1294 st = roundTime(clip->GetPlayEndTime());
1295 if (st >= t0 && st <= t1) {
1296 auto it = get_split(st);
1297 if (clip->GetTrimRight() != 0) {
1299 it->left = track.CopyClip(*clip,
false);
1300 it->left->SetTrimRight(.0);
1301 it->left->ClearLeft(clip->GetPlayEndTime());
1303 it->leftClipName = clip->GetName();
1307 auto cutlines = clip->GetCutLines();
1308 for (
auto &cut : cutlines) {
1309 const auto unrounded =
1310 clip->GetSequenceStartTime() + cut->GetSequenceStartTime();
1311 const double cs = roundTime(unrounded);
1314 if (cs >= t0 && cs <= t1) {
1316 cut->SetSequenceStartTime(cs);
1317 bool removed = clip->RemoveCutLine(unrounded);
1319 cuts.push_back(move(cut));
1324 const auto tolerance = 2.0 / track.GetRate();
1327 constexpr auto split =
false;
1330 track.HandleClear(t0, t1,
false, split, clearByTrimming);
1333 track.PasteWaveTrackAtSameTempo(t0, src, merge);
1336 if (merge && splits.size() > 0) {
1342 auto clips = track.SortedIntervalArray();
1347 for (
const auto clip : clips) {
1350 if (fabs(t1 - clip->GetPlayStartTime()) < tolerance) {
1351 if (prev && clip->HasEqualPitchAndSpeed(*prev))
1353 track.GetClipIndex(*prev), track.GetClipIndex(*clip));
1362 auto clips = track.SortedIntervalArray();
1367 for (
const auto clip : clips) {
1372 if (clip->HasEqualPitchAndSpeed(*prev))
1374 track.GetClipIndex(*prev), track.GetClipIndex(*clip));
1377 if (fabs(t0 - clip->GetPlayEndTime()) < tolerance)
1393 assert(target.GetTrimLeft() == 0);
1394 if (target.GetTrimLeft() != 0)
1400 assert(target.HasEqualPitchAndSpeed(src));
1402 auto trim = src.GetPlayEndTime() - src.GetPlayStartTime();
1403 auto success = target.Paste(target.GetPlayStartTime(), src);
1405 target.SetTrimLeft(trim);
1408 target.ShiftBy(-trim);
1414 assert(target.GetTrimRight() == 0);
1415 if (target.GetTrimRight() != 0)
1418 assert(target.HasEqualPitchAndSpeed(src));
1420 auto trim = src.GetPlayEndTime() - src.GetPlayStartTime();
1421 auto success = target.Paste(target.GetPlayEndTime(), src);
1423 target.SetTrimRight(trim);
1427 for (
const auto& split: splits) {
1428 auto at = roundTime(warper->
Warp(split.time));
1429 for (
const auto &&clip : track.Intervals()) {
1433 if (clip->SplitsPlayRegion(at))
1435 auto newClip =
CopyClip(*clip,
true);
1436 clip->ClearRight(at);
1437 newClip->ClearLeft(at);
1440 attachRight(*clip, *split.left);
1443 attachLeft(*newClip, *split.right);
1444 track.InsertInterval(move(newClip),
false);
1447 else if (clip->GetPlayStartSample() ==
1448 track.TimeToLongSamples(at) && split.right) {
1450 const auto trim = clip->GetTrimLeft();
1451 const auto seqStartTime = clip->GetSequenceStartTime();
1452 clip->Clear(seqStartTime, seqStartTime + trim);
1455 clip->ShiftBy(trim);
1456 attachLeft(*clip, *split.right);
1459 else if (clip->GetPlayEndSample() ==
1460 track.TimeToLongSamples(at) && split.left) {
1463 clip->GetPlayEndTime(), clip->GetSequenceEndTime());
1464 attachRight(*clip, *split.left);
1471 for (
const auto& split : splits)
1473 auto s = track.TimeToLongSamples(warper->
Warp(split.time));
1474 for (
auto &&clip : track.Intervals()) {
1475 if (split.rightClipName.has_value() && clip->GetPlayStartSample() == s)
1476 clip->SetName(*split.rightClipName);
1477 else if (split.leftClipName.has_value() && clip->GetPlayEndSample() == s)
1478 clip->SetName(*split.leftClipName);
1483 for (
const auto &&clip : track.Intervals()) {
1484 const double st = clip->GetPlayStartTime();
1485 const double et = clip->GetPlayEndTime();
1488 for (
auto &cut : cuts) {
1493 double cs = cut->GetSequenceStartTime();
1497 if (cs >= st && cs <= et) {
1498 cut->SetSequenceStartTime(warper->
Warp(cs) - st);
1499 clip->AddCutLine(cut);
1510 constexpr bool addCutLines =
false;
1511 constexpr bool split =
true;
1518 const auto begin = clips.begin();
1519 const auto pred = [&](
auto pClip){
return pClip.get() == &clip; };
1520 auto iter = std::find_if(
begin, clips.end(), pred);
1521 return std::distance(
begin, iter);
1527 if (distance < clips.size())
1528 clips.erase(clips.begin() + distance);
1533 const bool split,
const bool clearByTrimming)
1537 wxASSERT( t1 >= t0 );
1551 if (clip->PartlyWithinPlayRegion(t0, t1)) {
1552 addCutLines =
false;
1557 if (clip->CoversEntirePlayRegion(t0, t1))
1559 clipsToDelete.push_back(clip);
1560 else if (clip->IntersectsPlayRegion(t0, t1)) {
1565 clipsToDelete.push_back(clip);
1566 auto newClip =
CopyClip(*clip,
true);
1567 newClip->ClearAndAddCutLine(t0, t1);
1568 clipsToAdd.push_back(move(newClip));
1571 if (split || clearByTrimming) {
1574 if (clip->BeforePlayRegion(t0)) {
1579 clipsToDelete.push_back(clip);
1580 auto newClip =
CopyClip(*clip,
true);
1581 newClip->TrimLeft(t1 - clip->GetPlayStartTime());
1585 newClip->ShiftBy(t0 - t1);
1586 clipsToAdd.push_back(move(newClip));
1588 else if (clip->AfterPlayRegion(t1)) {
1593 clipsToDelete.push_back(clip);
1594 auto newClip =
CopyClip(*clip,
true);
1595 newClip->TrimRight(clip->GetPlayEndTime() - t0);
1597 clipsToAdd.push_back(move(newClip));
1603 auto leftClip =
CopyClip(*clip,
true);
1604 leftClip->TrimRight(clip->GetPlayEndTime() - t0);
1605 clipsToAdd.push_back(move(leftClip));
1607 auto rightClip =
CopyClip(*clip,
true);
1608 rightClip->TrimLeft(t1 - clip->GetPlayStartTime());
1612 rightClip->ShiftBy(t0 - t1);
1613 clipsToAdd.push_back(move(rightClip));
1615 clipsToDelete.push_back(clip);
1623 clipsToDelete.push_back(clip);
1624 auto newClip =
CopyClip(*clip,
true);
1627 newClip->Clear(t0,t1);
1629 clipsToAdd.push_back(move(newClip));
1638 for (
const auto &clip: clipsToDelete)
1646 if (clip->AtOrBeforePlayRegion(t1))
1647 clip->ShiftBy(-(t1 - t0));
1649 for (
auto &clip: clipsToAdd)
1656 if (newT1 > oldT1 &&
1662 if (newT1 > oldT1) {
1669 const auto offset = newT1 - oldT1;
1672 if (clip->GetPlayStartTime() > oldT1 - (1.0 / rate))
1673 clip->ShiftBy(offset);
1680 const auto duration = newT1 - oldT1;
1682 tmp->InsertSilence(0.0, duration);
1687 else if (newT1 < oldT1)
1688 Clear(newT1, oldT1);
1696 if (!tempo.has_value())
1703 double t0,
const WaveTrack& other,
bool merge)
1705 const auto otherNChannels = other.
NChannels();
1713 const auto insertDuration = endTime;
1714 auto &track = *
this;
1739 t0 = track.SnapToSample(t0);
1743 const auto clipAtT0 = track.GetIntervalAtTime(t0);
1746 const auto pitchAndSpeedMatch =
1747 !clipAtT0 || (clipAtT0->HasEqualPitchAndSpeed(*otherFirstClip) &&
1748 clipAtT0->HasEqualPitchAndSpeed(*otherLastClip));
1752 const bool singleClipMode =
1754 std::abs(startTime) < track.LongSamplesToTime(1) * 0.5 &&
1755 pitchAndSpeedMatch && merge;
1757 const auto rate = track.GetRate();
1758 if (insertDuration != 0 && insertDuration < 1.0 / rate)
1767 auto pastingFromTempTrack = !other.
GetOwner();
1772 XO(
"There is not enough room available to paste the selection"),
1773 XO(
"Warning"),
"Error:_Insufficient_space_in_track"
1777 if (editClipCanMove) {
1778 if (!singleClipMode)
1785 for (
const auto& clip : track.Intervals())
1786 if (clip->GetPlayStartTime() > t0 - (1.0 / rate))
1787 clip->ShiftBy(insertDuration);
1793 const auto clipAtT0 = track.GetClipAtTime(t0);
1794 const auto t = clipAtT0 ? clipAtT0->GetPlayEndTime() : t0;
1795 if (!track.IsEmpty(t, t + insertDuration))
1796 throw notEnoughSpaceException;
1803 if (singleClipMode && merge) {
1808 for (
const auto& clip : track.Intervals()) {
1809 if (editClipCanMove) {
1810 if (clip->SplitsPlayRegion(t0)) {
1819 if (clip->WithinPlayRegion(t0))
1830 if (!editClipCanMove) {
1833 for (
const auto& clip : track.Intervals()) {
1834 if (clip->GetPlayStartTime() > insideClip->GetPlayStartTime() &&
1835 insideClip->GetPlayEndTime() + insertDuration >
1836 clip->GetPlayStartTime())
1839 throw notEnoughSpaceException;
1842 if (
auto pClip = other.
GetClip(0)) {
1846 assert(insideClip->GetStretchRatio() == pClip->GetStretchRatio());
1849 assert(insideClip->NChannels() == pClip->NChannels());
1850 bool success = insideClip->Paste(t0, *pClip);
1861 if (!editClipCanMove &&
1862 !track.IsEmpty(t0, t0 + insertDuration - 1.0 / rate))
1865 throw notEnoughSpaceException;
1867 for (
const auto& clip : other.
Intervals()) {
1869 if (!clip->GetIsPlaceholder()) {
1870 const auto name = (pastingFromTempTrack)
1873 ? track.MakeNewClipName()
1874 : track.MakeClipCopyName(clip->GetName());
1875 const auto oldPlayStart = clip->GetPlayStartTime();
1876 const auto newSequenceStart =
1877 (oldPlayStart + t0) - clip->GetTrimLeft();
1878 const auto newClip =
CreateClip(newSequenceStart,
name, clip.get());
1879 newClip->Resample(rate);
1880 track.InsertInterval(move(newClip),
false);
1888 std::optional<double> oRate;
1890 return std::all_of(channels.begin(), channels.end(),
1895 const auto rate = pTrack->mLegacyRate;
1898 else if (*oRate != rate)
1907 return std::all_of(channels.begin(), channels.end(),
1909 return pTrack && pTrack->mLegacyFormat == mLegacyFormat;
1914 bool newClip,
bool backup,
bool allowEmpty)
1916 if (!backup && !clip->GetIsPlaceholder() && !allowEmpty && clip->IsEmpty())
1920 if (tempo.has_value())
1921 clip->OnProjectTempoChange(std::nullopt, *tempo);
1922 clips.push_back(std::move(clip));
1934 assert(!interval.has_value() ||
1935 interval->first <= interval->second);
1938 const auto startTime =
1941 const auto endTime =
1944 if (startTime >= endTime)
1949 clipAtT0 && clipAtT0->SplitsPlayRegion(startTime) &&
1950 clipAtT0->HasPitchOrSpeed())
1951 Split(startTime, startTime);
1953 clipAtT1 && clipAtT1->SplitsPlayRegion(endTime) &&
1954 clipAtT1->HasPitchOrSpeed())
1955 Split(endTime, endTime);
1959 while (clip && clip->GetPlayStartTime() < endTime)
1961 if (clip->HasPitchOrSpeed())
1962 srcIntervals.push_back(clip);
1972 if (
const auto other =
dynamic_cast<const WaveTrack*
>(&src))
1976 constexpr auto merge =
true;
1995 auto clipStart = pClip->GetPlayStartSample();
1996 auto clipEnd = pClip->GetPlayEndSample();
1997 if (clipEnd > start && clipStart <
end) {
1998 auto offset = std::max(start - clipStart,
sampleCount(0));
2000 auto length =
std::min(
end, clipEnd) - (clipStart + offset);
2001 pClip->SetSilence(offset, length);
2017 if (clips.empty()) {
2020 clip->InsertSilence(0, len);
2027 const auto end = clips.end();
2028 const auto it = std::find_if(clips.begin(),
end,
2029 [&](
const IntervalHolder &clip) { return clip->SplitsPlayRegion(t); } );
2033 (*it)->InsertSilence(t, len);
2036 for (
const auto &&clip : clips)
2037 if (clip->BeforePlayRegion(t))
2048 const size_t maxAtOnce = 1048576;
2049 std::vector<float> buffer;
2050 std::vector<samplePtr> buffers;
2055 for (
const auto &interval :
Intervals()) {
2056 double startTime = interval->Start();
2057 double endTime = interval->End();
2059 if (endTime < t0 || startTime > t1)
2063 if (buffer.empty()) {
2064 buffer.resize(maxAtOnce * width);
2065 buffers.resize(width);
2066 auto pBuffer = buffer.data();
2067 for (
size_t ii = 0; ii < width; ++ii, pBuffer += maxAtOnce)
2068 buffers[ii] =
reinterpret_cast<samplePtr>(pBuffer);
2071 const auto allZeroesAt = [&](
size_t i) {
2072 auto pData = buffer.data() + i;
2073 for (
size_t ii = 0; ii < width; ++ii, pData += maxAtOnce) {
2084 auto start = interval->TimeToSamples(std::max(.0, t0 - startTime));
2085 auto end = interval->TimeToSamples(
std::min(endTime, t1) - startTime);
2087 auto len = (
end - start);
2088 for (
decltype(len) done = 0; done < len; done += maxAtOnce) {
2091 auto bufferIt = buffers.begin();
2093 for (
auto channel : interval->Channels())
2094 channel->GetSamples(
2095 *bufferIt++,
floatSample, start + done, numSamples);
2097 for (
decltype(numSamples) i = 0; i < numSamples; ++i) {
2098 auto curSamplePos = start + done + i;
2101 if (seqStart == -1 && allZeroesAt(i))
2102 seqStart = curSamplePos;
2103 else if (curSamplePos ==
end - 1 || !allZeroesAt(i)) {
2104 if (seqStart != -1) {
2105 decltype(
end) seqEnd;
2108 if (curSamplePos ==
end - 1 && allZeroesAt(i))
2111 seqEnd = curSamplePos;
2112 if (seqEnd - seqStart + 1 > minSamples) {
2115 startTime + interval->SamplesToTime(seqStart),
2116 startTime + interval->SamplesToTime(seqEnd)
2127 for (
const auto ®ion : regions)
2140 for (
const auto &interval : intervals)
2141 if (interval->IntersectsPlayRegion(t0, t1))
2142 intervalsToJoin.push_back(interval);
2143 if (intervalsToJoin.size() < 2u)
2146 intervalsToJoin.begin() + 1, intervalsToJoin.end(),
2147 [first = intervalsToJoin[0]](
const auto& interval) {
2148 return !first->HasEqualPitchAndSpeed(*interval);
2157 for (
const auto &clip: intervals) {
2158 if (clip->IntersectsPlayRegion(t0, t1)) {
2160 auto it = clipsToDelete.begin(),
end = clipsToDelete.end();
2161 for (; it !=
end; ++it)
2162 if ((*it)->GetPlayStartTime() > clip->GetPlayStartTime())
2165 clipsToDelete.insert(it, clip);
2170 if (clipsToDelete.empty())
2173 const auto firstToDelete = clipsToDelete[0].get();
2174 auto t = firstToDelete->GetPlayStartTime();
2177 firstToDelete->GetSequenceStartTime(),
2178 firstToDelete->GetName());
2180 for (
const auto &clip : clipsToDelete) {
2185 if (clip->GetPlayStartTime() - t > (1.0 / rate))
2187 double addedSilence = (clip->GetPlayStartTime() - t);
2189 auto offset = clip->GetPlayStartTime();
2190 auto value = clip->GetEnvelope().GetValue(offset);
2191 newClip->AppendSilence(addedSilence, value);
2196 bool success = newClip->Paste(t, *clip);
2199 t = newClip->GetPlayEndTime();
2211 size_t len,
unsigned stride,
sampleFormat effectiveFormat)
2234 size_t len,
unsigned int stride,
sampleFormat effectiveFormat)
2241 buffers,
format, len, stride, effectiveFormat);
2249 auto startSample = clip->GetPlayStartSample();
2250 auto endSample = clip->GetPlayEndSample();
2251 if (s >= startSample && s < endSample)
2255 clip->GetBestBlockSize(s - clip->GetSequenceStartSample());
2260 return bestBlockSize;
2266 auto maxblocksize = std::accumulate(clips.begin(), clips.end(),
size_t{},
2267 [](
size_t acc,
auto pClip){
2268 return std::max(acc, pClip->GetMaxBlockSize()); });
2270 if (maxblocksize == 0)
2279 wxASSERT(maxblocksize > 0);
2281 return maxblocksize;
2288 .GetSequence(0)->GetIdealBlockSize();
2310 pInterval->RepairChannels();
2349 for (
const auto& pair : attrs)
2351 const auto& attr = pair.first;
2352 const auto& value = pair.second;
2357 if (!value.TryGet(dblValue) ||
2358 (dblValue < 1.0) || (dblValue > 1000000.0))
2364 else if (attr ==
Offset_attr && value.TryGet(dblValue))
2375 else if (attr ==
Gain_attr && value.TryGet(dblValue))
2377 else if (attr ==
Pan_attr && value.TryGet(dblValue) &&
2378 (dblValue >= -1.0) && (dblValue <= 1.0))
2380 else if (attr ==
Linked_attr && value.TryGet(nValue))
2413 const auto getClip = [
this]() ->
WaveClip & {
2425 return getClip().GetSequence(0);
2426 else if (tag ==
"envelope")
2427 return &getClip().GetEnvelope();
2435 auto pSeq = getClip().GetSequence(0);
2445 auto clip = std::make_shared<WaveClip>(1,
2447 const auto xmlHandler = clip.get();
2449 clips.push_back(std::move(clip));
2460 const auto channels = Channels();
2462 nChannels = channels.size();
2463 for (
const auto pChannel : channels)
2464 WriteOneXML(*pChannel, xmlFile,
iChannel++, nChannels);
2482 track.Track::WriteCommonXMLAttributes(xmlFile);
2492 const auto channelType = (nChannels == 0)
2502 const auto linkType =
static_cast<int>(
2503 (
iChannel == 0) && (nChannels == 2)
2510 const auto useLegacy = track.mLegacyRate != 0;
2513 track.WritableSampleTrack::WriteXMLAttributes(xmlFile);
2524 for (
const auto &clip : channel.
Intervals())
2525 clip->WriteXML(xmlFile);
2533 const auto width = pClip->NChannels();
2534 for (
size_t ii = 0; ii < width; ++ii)
2535 if (pClip->GetSequence(ii)->GetErrorOpening())
2536 return XO(
"A track has a corrupted sample sequence.");
2543 auto clips = Intervals();
2546 const auto begin = clips.begin(),
2547 iter = std::min_element(
begin, clips.end(),
2548 [](
const auto& a,
const auto b) {
2549 return a->GetPlayStartTime() < b->GetPlayStartTime();
2551 return GetClip(std::distance(
begin, iter));
2559 auto clips = Intervals();
2562 const auto begin = clips.begin(),
2563 iter = std::max_element(
begin, clips.end(),
2564 [](
const auto& a,
const auto b) {
2565 return a->GetPlayEndTime() < b->GetPlayEndTime();
2567 return GetClip(std::distance(
begin, iter));
2577 return { clips.begin(), clips.end() };
2603 bool mayThrow,
sampleCount* pNumWithinClips)
const
2608 assert(nBuffers <= 1);
2610 buffers,
format, start, len, backwards, fill, mayThrow, pNumWithinClips);
2621 bool mayThrow,
sampleCount* pNumWithinClips)
const
2624 assert(
iChannel + nBuffers <= nChannels);
2625 return std::all_of(buffers, buffers + nBuffers, [&](
samplePtr buffer) {
2627 buffer,
format, start, len, backwards, fill, mayThrow,
2635 bool backwards,
fillFormat fill,
bool mayThrow,
2643 bool doClear =
true;
2646 for (
const auto &clip: clips)
2648 if (start >= clip->GetPlayStartSample() && start+len <= clip->GetPlayEndSample())
2663 float * pBuffer = (
float*)buffer;
2664 for(
size_t i=0;i<len;i++)
2669 wxFAIL_MSG(
wxT(
"Invalid fill format"));
2674 for (
const auto &clip: clips)
2676 auto clipStart = clip->GetPlayStartSample();
2677 auto clipEnd = clip->GetPlayEndSample();
2679 if (clipEnd > start && clipStart < start+len)
2681 if (clip->HasPitchOrSpeed())
2685 auto samplesToCopy =
2686 std::min( start+len - clipStart, clip->GetVisibleSampleCount() );
2687 auto startDelta = clipStart - start;
2688 decltype(startDelta) inclipDelta = 0;
2691 inclipDelta = -startDelta;
2692 samplesToCopy -= inclipDelta;
2708 startDelta.as_size_t() *
2710 format, inclipDelta, samplesToCopy.as_size_t(), mayThrow ))
2713 samplesCopied += samplesToCopy;
2716 if( pNumWithinClips )
2717 *pNumWithinClips = samplesCopied;
2718 if (result ==
true && backwards)
2727 for (
const auto& channel :
Channels()) {
2728 result.push_back(channel->GetSampleView(t0, t1, mayThrow));
2736 std::vector<std::shared_ptr<const WaveClipChannel>>
2737 intersectingIntervals;
2738 for (
const auto &interval :
Intervals())
2739 if (interval->Intersects(t0, t1))
2740 intersectingIntervals.push_back(interval);
2741 if (intersectingIntervals.empty())
2745 intersectingIntervals.begin(), intersectingIntervals.end(),
2746 [](
const auto& a,
const auto& b) { return a->Start() < b->Start(); });
2747 std::vector<AudioSegmentSampleView> segments;
2748 segments.reserve(2 * intersectingIntervals.size() + 1);
2749 for (
auto i = 0u; i < intersectingIntervals.size();++i)
2751 const auto& interval = intersectingIntervals[i];
2752 const auto intervalStartTime = interval->Start();
2753 if (t0 < intervalStartTime)
2757 t0 = intervalStartTime;
2759 const auto intervalT0 = t0 - intervalStartTime;
2760 const auto intervalT1 =
std::min(t1, interval->End()) - intervalStartTime;
2761 if(intervalT1 > intervalT0)
2764 interval->GetSampleView(intervalT0, intervalT1, mayThrow);
2765 t0 += intervalT1 - intervalT0;
2766 segments.push_back(std::move(newSegment));
2783 auto clipStart = clip->GetPlayStartSample();
2784 auto clipEnd = clip->GetPlayEndSample();
2786 if (clipEnd > start && clipStart < start+len)
2789 if (clip->HasPitchOrSpeed())
2793 auto samplesToCopy =
2794 std::min( start+len - clipStart, clip->GetVisibleSampleCount() );
2795 auto startDelta = clipStart - start;
2796 decltype(startDelta) inclipDelta = 0;
2799 inclipDelta = -startDelta;
2800 samplesToCopy -= inclipDelta;
2816 format, inclipDelta, samplesToCopy.as_size_t(), effectiveFormat );
2831 result = std::max(result, pClip->GetSampleFormats().Effective());
2845 auto clips = pTrack->Intervals();
2846 return std::all_of(clips.begin(), clips.end(),
2847 [](
const auto &pClip){ return pClip->GetEnvelope().IsTrivial(); });
2851 double* buffer,
size_t bufferLen,
double t0,
bool backwards)
const
2857 double* buffer,
size_t bufferLen,
double t0,
bool backwards)
const
2864 t0 -= bufferLen / pTrack->GetRate();
2875 for (
decltype(bufferLen) i = 0; i < bufferLen; i++)
2880 double startTime = t0;
2881 const auto rate = pTrack->GetRate();
2882 auto tstep = 1.0 / rate;
2883 double endTime = t0 + tstep * bufferLen;
2884 for (
const auto &clip: pTrack->Intervals())
2887 auto dClipStartTime = clip->GetPlayStartTime();
2888 auto dClipEndTime = clip->GetPlayEndTime();
2889 if ((dClipStartTime < endTime) && (dClipEndTime > startTime))
2892 auto rlen = bufferLen;
2895 if (rt0 < dClipStartTime)
2899 auto nDiff = (
sampleCount)floor((dClipStartTime - rt0) * rate + 0.5);
2900 auto snDiff = nDiff.as_size_t();
2902 wxASSERT(snDiff <= rlen);
2904 rt0 = dClipStartTime;
2907 if (rt0 + rlen*tstep > dClipEndTime)
2909 auto nClipLen = clip->GetPlayEndSample() - clip->GetPlayStartSample();
2920 rlen =
std::min(rlen,
size_t(floor(0.5 + (dClipEndTime - rt0) / tstep)));
2924 clip->GetEnvelope().GetValues(rbuf, rlen, rt0, tstep);
2928 std::reverse(buffer, buffer + bufferLen);
2936 auto p = std::find_if(
2937 clips.rbegin(), clips.rend(), [&](
const auto &pClip) {
2938 return pClip->WithinPlayRegion(time);
2940 return p != clips.rend() ? *p :
nullptr;
2948 std::make_shared<WaveClip>(*pToCopy, mpFactory, copyCutlines);
2949 pNewClip->SetName(
name);
2950 pNewClip->SetSequenceStartTime(offset);
2954 return DoCreateClip(offset,
name);
2960 return CreateClip(toCopy.GetSequenceStartTime(),
2961 toCopy.GetName(), &toCopy, copyCutlines);
2972 auto clip = std::make_shared<WaveClip>(NChannels(),
2973 mpFactory, GetSampleFormat(),
GetRate());
2974 clip->SetName(
name);
2975 clip->SetSequenceStartTime(offset);
2978 if (tempo.has_value())
2979 clip->OnProjectTempoChange(std::nullopt, *tempo);
2980 assert(clip->NChannels() == NChannels());
2986 const auto &intervals = Intervals();
2987 if (intervals.empty()) {
2989 const auto name = MakeNewClipName();
2990 auto pInterval = CreateClip(origin,
name);
2991 InsertInterval(pInterval,
true,
true);
2995 return mClips.back();
3001 if (mClips.empty()) {
3002 auto pInterval = CreateClip(
3004 InsertInterval(pInterval,
true,
true);
3008 auto end = mClips.end(),
3009 it = max_element(mClips.begin(),
end,
3010 [](
const auto &pClip1,
const auto &pClip2){
3011 return pClip1->GetPlayStartTime() < pClip2->GetPlayStartTime();
3024 [&](
const auto &pOtherClip){
return &clip == pOtherClip.get(); };
3025 auto begin = clips.begin(),
3027 iter = std::find_if(
begin,
end, test);
3028 return std::distance(
begin, iter);
3037 const std::vector<Interval*> &movingClips,
3039 double *allowedAmount )
3042 *allowedAmount = amount;
3044 const auto &moving = [&](
Interval *clip){
3047 return movingClips.end() !=
3048 std::find(movingClips.begin(), movingClips.end(), clip);
3052 if ( moving( c.get() ) )
3054 for (
const auto clip : movingClips) {
3055 if (c->GetPlayStartTime() < clip->GetPlayEndTime() + amount &&
3056 c->GetPlayEndTime() > clip->GetPlayStartTime() + amount)
3063 if (c->GetPlayStartTime() - clip->GetPlayEndTime() < *allowedAmount)
3064 *allowedAmount = c->GetPlayStartTime() - clip->GetPlayEndTime();
3065 if (*allowedAmount < 0)
3069 if (c->GetPlayEndTime() - clip->GetPlayStartTime() > *allowedAmount)
3070 *allowedAmount = c->GetPlayEndTime() - clip->GetPlayStartTime();
3071 if (*allowedAmount > 0)
3080 if (*allowedAmount == amount)
3096 const Interval& candidateClip,
double& slideBy,
double tolerance)
const
3102 const auto candidateClipStartTime = candidateClip.GetPlayStartTime();
3103 const auto candidateClipEndTime = candidateClip.GetPlayEndTime();
3104 const auto t0 =
SnapToSample(candidateClipStartTime + slideBy);
3105 const auto t1 =
SnapToSample(candidateClipEndTime + slideBy);
3106 std::vector<double> overlaps;
3108 clips.begin(), clips.end(), std::back_inserter(overlaps),
3109 [&](
const auto& pClip) {
3110 return pClip->IntersectsPlayRegion(t0, t1) ?
3111 std::min(pClip->GetPlayEndTime(), t1) -
3112 std::max(pClip->GetPlayStartTime(), t0) :
3115 const auto maxOverlap = std::max_element(overlaps.begin(), overlaps.end());
3116 if (*maxOverlap > tolerance)
3118 auto iter = clips.begin();
3119 std::advance(iter, std::distance(overlaps.begin(), maxOverlap));
3120 const auto overlappedClip = *iter;
3121 const auto requiredOffset = slideBy +
3122 *maxOverlap * (overlappedClip->GetPlayStartTime() < t0 ? 1 : -1);
3125 clips.begin(), clips.end(),
3126 [&](
const auto& pClip)
3128 const auto result = pClip->IntersectsPlayRegion(
3129 SnapToSample(candidateClipStartTime + requiredOffset),
3130 SnapToSample(candidateClipEndTime + requiredOffset));
3134 slideBy = requiredOffset;
3149 for (
const auto &&c : Intervals()) {
3150 if (c->SplitsPlayRegion(t)) {
3151 t = SnapToSample(t);
3152 auto newClip = CopyClip(*c,
true);
3154 newClip->TrimLeftTo(t);
3155 auto result = std::pair{ c, newClip };
3159 InsertInterval(move(newClip),
false);
3169 const auto clip1 =
GetClip(clipidx1);
3170 const auto clip2 =
GetClip(clipidx2);
3172 if (!clip1 || !clip2)
3175 if (!clip1->HasEqualPitchAndSpeed(*clip2))
3180 bool success = clip1->Paste(clip1->GetPlayEndTime(), *clip2);
3194 dstIntervals.reserve(srcIntervals.size());
3196 srcIntervals.begin(), srcIntervals.end(),
3197 std::back_inserter(dstIntervals), [&](
const IntervalHolder& interval) {
3198 return GetRenderedCopy(interval,
3199 reportProgress, mpFactory, GetSampleFormat());
3204 for (
auto i = 0; i < srcIntervals.size(); ++i)
3212 using Set = std::unordered_set<WaveClipHolder>;
3213 return clips.size() == Set{ clips.begin(), clips.end() }.size();
3218 bool newClip,
bool allowEmpty)
3221 constexpr bool backup =
false;
3231 iter = find(
mClips.begin(),
end, interval);
3240 assert(oldOne->NChannels() == newOne->NChannels());
3243 newOne->SetName(oldOne->GetName());
3251 pClip->Resample(rate, progress);
3260 for (
const auto &pChannel :
Channels()) {
3261 const auto buffer = buffers[ii++];
3263 result = pChannel->SetFloats(buffer, start, len, effectiveFormat)
3273 const auto comp = [](
const auto &a,
const auto &b) {
3274 return a->GetPlayStartTime() < b->GetPlayStartTime(); };
3275 std::sort(clips.begin(), clips.end(), comp);
3281 const auto &intervals = Intervals();
3283 copy(intervals.begin(), intervals.end(), back_inserter(result));
3284 sort(result.begin(), result.end(), [](
const auto &pA,
const auto &pB){
3285 return pA->GetPlayStartTime() < pB->GetPlayStartTime(); });
3293 copy(intervals.begin(), intervals.end(), back_inserter(result));
3294 sort(result.begin(), result.end(), [](
const auto &pA,
const auto &pB){
3295 return pA->GetPlayStartTime() < pB->GetPlayStartTime(); });
3308 auto iter = pOwner->Find(
this);
3309 assert(
this == *iter);
3311 assert(iter != pOwner->end());
3312 auto pRight =
dynamic_cast<WaveTrack*
>(*iter);
3313 assert(pRight && pRight->NChannels() == 1);
3326 auto iterMe =
mClips.begin(),
3328 auto iterRight = pRight->mClips.begin(),
3329 endRight = pRight->mClips.end();
3330 while (iterMe != endMe && iterRight != endRight) {
3331 (*iterMe)->MakeStereo(std::move(**iterRight), mustAlign);
3335 assert(!mustAlign || (iterMe == endMe && iterRight == endRight));
3337 while (iterRight != endRight) {
3339 mClips.emplace_back(move(*iterRight));
3345 pOwner->Remove(*pRight);
3349 return std::make_shared< WaveTrackFactory >(
3381 L
"/GUI/TrackNames/DefaultTrackName",
3395 bool editClipsCanMove;
3400 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 &)