Audacity 3.2.0
WaveTrack.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 WaveTrack.cpp
6
7 Dominic Mazzoni
8
9*******************************************************************//****************************************************************//****************************************************************/
21
29#include "WaveTrack.h"
30
31#include "WideClip.h"
32#include "WaveClip.h"
33
34#include <wx/defs.h>
35#include <wx/debug.h>
36#include <wx/log.h>
37
38#include <algorithm>
39#include <float.h>
40#include <math.h>
41#include <numeric>
42#include <optional>
43#include <type_traits>
44
45#include "float_cast.h"
46
49#include "Envelope.h"
50#include "Sequence.h"
52
53#include "Project.h"
54#include "ProjectRate.h"
55#include "SampleBlock.h"
56
57#include "BasicUI.h"
58#include "Prefs.h"
59#include "QualitySettings.h"
60#include "SyncLock.h"
61#include "TimeWarper.h"
62
63
65
67
68using std::max;
69
71
72bool WaveChannelInterval::Intersects(double t0, double t1) const
73{
74 return GetNarrowClip().IntersectsPlayRegion(t0, t1);
75}
76
78{
80}
81
83{
85}
86
88WaveChannelInterval::GetSampleView(double t0, double t1, bool mayThrow) const
89{
90 constexpr auto iChannel = 0u;
91 // TODO wide wave tracks: use the real channel number.
92 return GetNarrowClip().GetSampleView(iChannel, t0, t1, mayThrow);
93}
94
96{
97 // Always the left clip's envelope
98 return *mWideClip.GetEnvelope();
99}
100
102{
104}
105
107{
108 return GetNarrowClip().GetRate();
109}
110
112{
114}
115
117{
118 return GetNarrowClip().GetPlayEndTime();
119}
120
122{
123 return GetNarrowClip().TimeToSamples(time);
124}
125
127{
129}
130
132{
133 return GetNarrowClip().GetTrimLeft();
134}
135
137{
138 return GetNarrowClip().GetTrimRight();
139}
140
142 sampleCount start, size_t len, bool mayThrow) const
143{
144 // Not just a pass-through, but supply the first argument
145 // TODO wide wave tracks -- pass miChannel not 0
146 return GetNarrowClip().GetSamples(0, buffer, format, start, len, mayThrow);
147}
148
150 sampleCount start, size_t length, bool mayThrow) const
151{
152 // Not just a pass-through, but supply the first argument
153 // TODO wide wave tracks -- pass miChannel not 0
154 return GetNarrowClip().GetSampleView(0, start, length, mayThrow);
155}
156
158{
159 // TODO wide wave tracks -- use miChannel
160 const auto pSequence = GetNarrowClip().GetSequence(0);
161 // Assume sufficiently wide clip
162 assert(pSequence);
163 return *pSequence;
164}
165
167{
168 // TODO wide wave tracks -- use miChannel
169 return GetNarrowClip().GetAppendBuffer(0);
170}
171
173{
175}
176
178{
179 return GetNarrowClip().GetColourIndex();
180}
181
183 const std::shared_ptr<WaveClip> &pClip,
184 const std::shared_ptr<WaveClip> &pClip1
185) : WideChannelGroupInterval{ group,
186 pClip->GetPlayStartTime(), pClip->GetPlayEndTime() }
187 , mpClip{ pClip }
188 , mpClip1{ pClip1 }
189{
190}
191
193 const ChannelGroup& group, size_t width,
195 : Interval(
196 group, std::make_shared<WaveClip>(1, factory, format, rate, 0),
197 width == 2 ?
198 std::make_shared<WaveClip>(1, factory, format, rate, 0) :
199 nullptr)
200{
201}
202
204
206 constSamplePtr buffer[], sampleFormat format, size_t len)
207{
208 for (unsigned channel = 0; channel < NChannels(); ++channel)
209 GetClip(channel)->AppendNewBlock(buffer[channel], format, len);
210}
211
213{
214 ForEachClip([](auto& clip) { clip.Flush(); });
215}
216
218{
219 for(unsigned channel = 0; channel < NChannels(); ++channel)
220 GetClip(channel)->TrimLeftTo(t);
221}
222
224{
225 for(unsigned channel = 0; channel < NChannels(); ++channel)
226 GetClip(channel)->TrimRightTo(t);
227}
228
230{
231 for(unsigned channel = 0; channel < NChannels(); ++channel)
232 GetClip(channel)->SetTrimLeft(t);
233}
234
236{
237 for(unsigned channel = 0; channel < NChannels(); ++channel)
238 GetClip(channel)->SetTrimRight(t);
239}
240
242{
243 for(unsigned channel = 0; channel < NChannels(); ++channel)
244 GetClip(channel)->ClearLeft(t);
245}
246
248{
249 for(unsigned channel = 0; channel < NChannels(); ++channel)
250 GetClip(channel)->ClearRight(t);
251}
252
254{
255 for(unsigned channel = 0; channel < NChannels(); ++channel)
256 GetClip(channel)->StretchLeftTo(t);
257}
258
260{
261 for(unsigned channel = 0; channel < NChannels(); ++channel)
262 GetClip(channel)->StretchRightTo(t);
263}
264
266 const std::function<void(double)>& reportProgress, const ChannelGroup& group,
268{
269 if (GetStretchRatio() == 1)
270 return std::make_shared<Interval>(group, mpClip, mpClip1);
271
272 const auto dst = std::make_shared<Interval>(
273 group, NChannels(), factory, mpClip->GetRate(), format);
274
275 const auto originalPlayStartTime = GetPlayStartTime();
276 const auto originalPlayEndTime = GetPlayEndTime();
277 const auto stretchRatio = GetStretchRatio();
278
279 auto success = false;
280 Finally Do { [&] {
281 if (!success)
282 {
283 TrimLeftTo(originalPlayStartTime);
284 TrimRightTo(originalPlayEndTime);
285 }
286 } };
287
288 // Leave 1 second of raw, unstretched audio before and after visible region
289 // to give the algorithm a chance to be in a steady state when reaching the
290 // play boundaries.
291 const auto tmpPlayStartTime =
292 std::max(GetSequenceStartTime(), originalPlayStartTime - stretchRatio);
293 const auto tmpPlayEndTime =
294 std::min(GetSequenceEndTime(), originalPlayEndTime + stretchRatio);
295 TrimLeftTo(tmpPlayStartTime);
296 TrimRightTo(tmpPlayEndTime);
297
298 WideClip wideClip { mpClip, mpClip1 };
299
300 constexpr auto sourceDurationToDiscard = 0.;
301 constexpr auto blockSize = 1024;
302 const auto numChannels = NChannels();
303 ClipTimeAndPitchSource stretcherSource { wideClip, sourceDurationToDiscard,
306 params.timeRatio = stretchRatio;
307 StaffPadTimeAndPitch stretcher { mpClip->GetRate(), numChannels,
308 stretcherSource, std::move(params) };
309
310 // Post-rendering sample counts, i.e., stretched units
311 const auto totalNumOutSamples =
312 sampleCount { wideClip.GetVisibleSampleCount().as_double() *
313 stretchRatio };
314
315 sampleCount numOutSamples { 0 };
316 AudioContainer container(blockSize, numChannels);
317
318 while (numOutSamples < totalNumOutSamples)
319 {
320 const auto numSamplesToGet =
321 limitSampleBufferSize(blockSize, totalNumOutSamples - numOutSamples);
322 stretcher.GetSamples(container.Get(), numSamplesToGet);
323 constSamplePtr data[2];
324 data[0] = reinterpret_cast<constSamplePtr>(container.Get()[0]);
325 if (NChannels() == 2)
326 data[1] = reinterpret_cast<constSamplePtr>(container.Get()[1]);
327 dst->Append(data, floatSample, numSamplesToGet);
328 numOutSamples += numSamplesToGet;
329 if (reportProgress)
330 reportProgress(
331 numOutSamples.as_double() / totalNumOutSamples.as_double());
332 }
333 dst->Flush();
334
335 // Now we're all like `this` except unstretched. We can clear leading and
336 // trailing, stretching transient parts.
337 dst->SetPlayStartTime(tmpPlayStartTime);
338 dst->ClearLeft(originalPlayStartTime);
339 dst->ClearRight(originalPlayEndTime);
340
341 // We don't preserve cutlines but the relevant part of the envelope.
342 Envelope dstEnvelope = GetEnvelope();
343 const auto samplePeriod = 1. / mpClip->GetRate();
344 dstEnvelope.CollapseRegion(
345 originalPlayEndTime, GetSequenceEndTime() + samplePeriod, samplePeriod);
346 dstEnvelope.CollapseRegion(0, originalPlayStartTime, samplePeriod);
347 dstEnvelope.SetOffset(originalPlayStartTime);
348 dst->SetEnvelope(dstEnvelope);
349
350 success = true;
351
352 assert(dst->GetStretchRatio() == 1);
353 return dst;
354}
355
357{
358 for (unsigned channel = 0; channel < NChannels(); ++channel)
359 {
360 if (!GetClip(channel)->StretchRatioEquals(value))
361 return false;
362 }
363 return true;
364}
365
367{
368 ForEachClip([&](auto& clip) { clip.SetName(name); });
369}
370
371const wxString& WaveTrack::Interval::GetName() const
372{
373 //TODO wide wave tracks: assuming that all 'narrow' clips share common name
374 return mpClip->GetName();
375}
376
378{
379 ForEachClip([&](auto& clip) { clip.SetColourIndex(index); });
380}
381
383{
384 //TODO wide wave tracks: assuming that all 'narrow' clips share common color index
385 return mpClip->GetColourIndex();
386}
387
389{
390 ForEachClip([&](auto& clip) { clip.SetPlayStartTime(time); });
391}
392
394{
395 //TODO wide wave tracks: assuming that all 'narrow' clips share common beginning
396 return mpClip->GetPlayStartTime();
397}
398
400{
401 // TODO wide wave tracks: assuming that all 'narrow' clips share common
402 // beginning
403 return mpClip->GetPlayEndTime();
404}
405
406bool WaveTrack::Interval::IntersectsPlayRegion(double t0, double t1) const
407{
408 // TODO wide wave tracks: assuming that all 'narrow' clips share common
409 // boundaries
410 return mpClip->IntersectsPlayRegion(t0, t1);
411}
412
414{
415 return mpClip->WithinPlayRegion(t);
416}
417
419{
420 //TODO wide wave tracks: assuming that all 'narrow' clips share common stretch ratio
421 return mpClip->GetStretchRatio();
422}
423
425{
426 return mpClip->TimeToSamples(time);
427}
428
430{
431 return mpClip->SamplesToTime(s);
432}
433
435{
436 return mpClip->GetSequenceStartTime();
437}
438
440{
441 ForEachClip([t](auto& clip) { clip.SetSequenceStartTime(t); });
442}
443
445{
446 return mpClip->GetSequenceEndTime();
447}
448
450{
451 //TODO wide wave tracks: assuming that all 'narrow' clips share common trims
452 return mpClip->GetTrimLeft();
453}
454
456{
457 //TODO wide wave tracks: assuming that all 'narrow' clips share common trims
458 return mpClip->GetTrimRight();
459}
460
462{
463 return mpClip->GetIsPlaceholder();
464}
465
467{
468 return *mpClip->GetEnvelope();
469}
470
472{
473 mpClip->SetEnvelope(std::make_unique<Envelope>(envelope));
474}
475
476void WaveTrack::Interval::ForEachClip(const std::function<void(WaveClip&)>& op)
477{
478 for(unsigned channel = 0,
479 channelCount = NChannels();
480 channel < channelCount; ++channel)
481 {
482 op(*GetClip(channel));
483 }
484}
485
486std::shared_ptr<ChannelInterval>
488{
489 if (iChannel < NChannels()) {
490 // TODO wide wave tracks: there will be only one, wide clip
491 const auto pClip = (iChannel == 0 ? mpClip : mpClip1);
492 return std::make_shared<WaveChannelInterval>(*mpClip,
493 *pClip, iChannel);
494 }
495 return {};
496}
497
498std::shared_ptr<const WaveTrack::Interval>
499WaveTrack::GetNextInterval(const Interval& interval, PlaybackDirection searchDirection) const
500{
501 std::shared_ptr<const Interval> result;
502 auto bestMatchTime = searchDirection == PlaybackDirection::forward
503 ? std::numeric_limits<double>::max()
504 : std::numeric_limits<double>::lowest();
505
506 for(const auto& other : Intervals())
507 {
508 if((searchDirection == PlaybackDirection::forward &&
509 (other->Start() > interval.Start() && other->Start() < bestMatchTime))
510 ||
511 (searchDirection == PlaybackDirection::backward &&
512 (other->Start() < interval.Start() && other->Start() > bestMatchTime)))
513 {
514 result = other;
515 bestMatchTime = other->Start();
516 }
517 }
518 return result;
519}
520
522 const Interval& interval, PlaybackDirection searchDirection)
523{
524 return std::const_pointer_cast<Interval>(
525 std::as_const(*this).GetNextInterval(interval, searchDirection));
526}
527
529{
530 IntervalHolder result;
531 for (const auto& interval : Intervals())
532 if (interval->WithinPlayRegion(t))
533 return interval;
534 return nullptr;
535}
536
537namespace {
539 WaveTrackData() = default;
542 ~WaveTrackData() override;
544
545 static WaveTrackData &Get(WaveTrack &track);
546 static const WaveTrackData &Get(const WaveTrack &track);
547
548 int GetWaveColorIndex() const;
549 void SetWaveColorIndex(int index);
550
551 double GetOrigin() const;
552 void SetOrigin(double origin);
553
555 void SetSampleFormat(sampleFormat format);
556
557 float GetGain() const;
558 void SetGain(float value);
559 float GetPan() const;
560 void SetPan(float value);
561
562 int GetRate() const;
563 void SetRate(int value);
564
565private:
567 std::atomic<float> mGain{ 1.0f };
569 std::atomic<float> mPan{ 0.0f };
570
571 int mRate{ 44100 };
572 double mOrigin{ 0.0 };
573 int mWaveColorIndex{ 0 };
575};
576
579 [](auto &) { return std::make_unique<WaveTrackData>(); } };
580
582WaveTrackData::WaveTrackData(const WaveTrackData &other) {
583 SetGain(other.GetGain());
584 SetPan(other.GetPan());
585 mRate = other.mRate;
586 mOrigin = other.mOrigin;
587 mWaveColorIndex = other.mWaveColorIndex;
588 mFormat = other.mFormat;
589}
590
591WaveTrackData::~WaveTrackData() = default;
592
593std::unique_ptr<ClientData::Cloneable<>> WaveTrackData::Clone() const {
594 return std::make_unique<WaveTrackData>(*this);
595}
596
598 return track.GetGroupData().Attachments
600}
601
603{
604 return Get(const_cast<WaveTrack &>(track));
605}
606
607int WaveTrackData::GetWaveColorIndex() const
608{
609 return mWaveColorIndex;
610}
611
612void WaveTrackData::SetWaveColorIndex(int index)
613{
614 mWaveColorIndex = index;
615}
616
617double WaveTrackData::GetOrigin() const
618{
619 return mOrigin;
620}
621void WaveTrackData::SetOrigin(double origin)
622{
623 mOrigin = origin;
624}
625
626sampleFormat WaveTrackData::GetSampleFormat() const
627{
628 return mFormat;
629}
630
631void WaveTrackData::SetSampleFormat(sampleFormat format)
632{
633 mFormat = format;
634}
635
636float WaveTrackData::GetGain() const
637{
638 return mGain.load(std::memory_order_relaxed);
639}
640
641void WaveTrackData::SetGain(float value)
642{
643 mGain.store(value, std::memory_order_relaxed);
644}
645
646float WaveTrackData::GetPan() const
647{
648 return mPan.load(std::memory_order_relaxed);
649}
650
651void WaveTrackData::SetPan(float value)
652{
653 mPan.store(value, std::memory_order_relaxed);
654}
655
657{
658 return mRate;
659}
660
661void WaveTrackData::SetRate(int value)
662{
663 mRate = value;
664}
665
667{
668 if (a.size() != b.size())
669 return false;
670
671 const auto compare = [](const WaveClip* a, const WaveClip* b) {
672 // clips are aligned if both sequence start/end
673 // points and play start/end points of the first clip match
674 // the corresponding points of the other clip
675 return a->GetPlayStartTime() == b->GetPlayStartTime() &&
676 a->GetSequenceStartTime() == b->GetSequenceStartTime() &&
677 a->GetPlayEndTime() == b->GetPlayEndTime() &&
678 a->GetSequenceEndTime() == b->GetSequenceEndTime();
679 };
680
681 return std::mismatch(a.begin(), a.end(), b.begin(), compare).first == a.end();
682}
683
684//Handles possible future file values
686{
687 if (value < 0)
689 else if (value > 3)
691 return static_cast<Track::LinkType>(value);
692}
693
694}
695
697{
699 return std::max(ProjectRate::Get(project).GetRate(),
700 tracks.Any<const WaveTrack>().max(&WaveTrack::GetRate))
701 / 2.0;
702}
703
704static auto DefaultName = XO("Audio");
705
706WaveChannel::~WaveChannel() = default;
707
709{
711
712 if (name.empty() || ( name == DefaultName.MSGID() ))
713 // When nothing was specified,
714 // the default-default is whatever translation of...
715 /* i18n-hint: The default name for an audio track. */
716 return DefaultName.Translation();
717 else
718 return name;
719}
720
722 "wavetrack",
724};
725
726std::shared_ptr<WaveTrack> WaveTrackFactory::Create()
727{
728 return Create(QualitySettings::SampleFormatChoice(), mRate.GetRate());
729}
730
731std::shared_ptr<WaveTrack> WaveTrackFactory::Create(sampleFormat format, double rate)
732{
733 return std::make_shared<WaveTrack>(mpFactory, format, rate);
734}
735
737{
738 return Create(nChannels, QualitySettings::SampleFormatChoice(), mRate.GetRate());
739}
740
742{
743 auto channels = std::vector<std::shared_ptr<Track>>{ };
744 std::generate_n(
745 std::back_inserter(channels),
746 nChannels,
747 [&] { return Create(format, rate); }
748 );
749 if(nChannels == 2)
750 return TrackList::Temporary(nullptr, channels[0], channels[1]);
751 return TrackList::Temporary(nullptr, channels);
752}
753
755{
756 auto channels = std::vector<std::shared_ptr<Track>>{};
757 std::generate_n(
758 std::back_inserter(channels),
759 nChannels,
760 [&]{ return proto.EmptyCopy(mpFactory, false); }
761 );
762 if(nChannels == 2)
763 return TrackList::Temporary(nullptr, channels[0], channels[1]);
764 return TrackList::Temporary(nullptr, channels);
765}
766
768{
769 auto &trackFactory = WaveTrackFactory::Get( project );
770 auto &tracks = TrackList::Get( project );
771 auto result = tracks.Add(trackFactory.Create());
772 result->AttachedTrackObjects::BuildAll();
773 return result;
774}
775
777 sampleFormat format, double rate )
778 : mpFactory(pFactory)
779{
781
782 WaveTrackData::Get(*this).SetSampleFormat(format);
783 DoSetRate(static_cast<int>(rate));
784}
785
787 : WritableSampleTrack(orig, std::move(a))
788 , mpFactory( orig.mpFactory )
789{
791 for (const auto &clip : orig.mClips)
792 InsertClip(std::make_shared<WaveClip>(*clip, mpFactory, true));
793}
794
796{
797 return 1;
798}
799
801{
802 if (IsLeader() && GetOwner()) {
803 auto result = TrackList::NChannels(*this);
804 assert(result > 0);
805 return result;
806 }
807 else
808 return 1;
809}
810
812{
813 if (TrackList::NChannels(*this) == 1)
815 else if (IsLeader())
817 else
818 // TODO: more-than-two-channels
820}
821
822// Copy the track metadata but not the contents.
823void WaveTrack::Init(const WaveTrack &orig)
824{
826 mpFactory = orig.mpFactory;
827}
828
830{
831 assert(IsLeader());
832 assert(orig.IsLeader());
833 assert(NChannels() == orig.NChannels());
834 const auto channels = TrackList::Channels(this);
835 auto iter = TrackList::Channels(&orig).begin();
836 for (const auto pChannel : channels)
837 pChannel->Init(**iter++);
838}
839
841{
842}
843
845void WaveTrack::MoveTo(double origin)
846{
847 double delta = origin - GetStartTime();
848 assert(IsLeader());
849 for (const auto pChannel : TrackList::Channels(this)) {
850 for (const auto &clip : pChannel->mClips)
851 // assume No-fail-guarantee
852 clip->ShiftBy(delta);
853 }
854 WaveTrackData::Get(*this).SetOrigin(origin);
855}
856
858 const std::optional<double>& oldTempo, double newTempo)
859{
860 assert(IsLeader());
861 for (const auto pChannel : TrackList::Channels(this))
862 for (const auto& clip : pChannel->mClips)
863 clip->OnProjectTempoChange(oldTempo, newTempo);
864}
865
867WaveTrack::DuplicateWithOtherTempo(double newTempo, WaveTrack*& leader) const
868{
869 const auto srcCopyList = Duplicate();
870 leader = *srcCopyList->Any<WaveTrack>().begin();
871 leader->OnProjectTempoChange(newTempo);
872 return srcCopyList;
873}
874
876{
877 assert(!doFix || IsLeader());
878
879
880 const auto removeZeroClips = [](WaveClipHolders& clips) {
881 // Check for zero-length clips and remove them
882 for (auto it = clips.begin(); it != clips.end();)
883 {
884 if ((*it)->IsEmpty())
885 it = clips.erase(it);
886 else
887 ++it;
888 }
889 };
890
892
893 const auto linkType = GetLinkType();
894 if (linkType != LinkType::None) {
895 auto next = *TrackList::Channels(this).first.advance(1);
896 if (next == nullptr) {
897 //next track is absent or not a wave track, fix and report error
898 if (doFix) {
899 wxLogWarning(L"Right track %s is expected to be a WaveTrack."
900 "\n Removing link from left wave track %s.",
901 next->GetName(), GetName());
903 }
904 err = true;
905 }
906 else if (doFix) {
907 // non-error upgrades happen here
908 if (!AreAligned(SortedClipArray(), next->SortedClipArray()) ||
910 {
912 }
913 else
914 {
916 // Be sure to lose any right channel group data that might
917 // have been made during during deserialization of the channel
918 // before joining it
919 next->DestroyGroupData();
920 //clean up zero clips only after alignment check has completed
921 //this can't break alignment as there should be a "twin"
922 //in the right channel which will also be removed, otherwise
923 //track will be unlinked
924 removeZeroClips(next->mClips);
925 }
926 }
927 }
928 if (doFix) {
929 // More non-error upgrading
930 // Set the common channel group rate from the leader's rate
931 if (mLegacyRate > 0)
932 {
934 mLegacyRate = 0;
936 WaveTrackData::Get(*this).SetSampleFormat(mLegacyFormat);
937 }
938 removeZeroClips(mClips);
939 }
940 return !err;
941}
942
944{
945 static const Track::TypeInfo info{
946 { "wave", "wave", XO("Wave Track") },
948 return info;
949}
950
951auto WaveTrack::GetTypeInfo() const -> const TypeInfo &
952{
953 return typeInfo();
954}
955
957{
958 return typeInfo();
959}
960
962 AudacityProject &project, TrackList &list) const
963{
964 assert(IsLeader());
965 auto &trackFactory = WaveTrackFactory::Get(project);
966 auto &pSampleBlockFactory = trackFactory.GetSampleBlockFactory();
967 Track::Holder pFirstTrack;
968 const WaveTrack *pFirstChannel{};
969 for (const auto pChannel : TrackList::Channels(this)) {
970 auto pNewTrack = pChannel->EmptyCopy(pSampleBlockFactory);
971 list.Add(pNewTrack);
972 assert(pNewTrack->IsLeader() == pChannel->IsLeader());
973 if (!pFirstTrack) {
974 pFirstTrack = pNewTrack;
975 pFirstChannel = pChannel;
976 }
977 }
978 pFirstTrack->Paste(0.0, *pFirstChannel);
979 return pFirstTrack;
980}
981
983{
984 return mClips.size();
985}
986
987std::shared_ptr<WideChannelGroupInterval>
989{
990 if (iInterval < NIntervals()) {
991 WaveClipHolder pClip = mClips[iInterval],
992 pClip1;
993 // TODO wide wave tracks
994 // This assumed correspondence of clips may be wrong if they misalign
995 if (auto right = ChannelGroup::GetChannel<WaveTrack>(1)
996 ; right && iInterval < right->mClips.size()
997 )
998 pClip1 = right->mClips[iInterval];
999 return std::make_shared<Interval>(*this, pClip, pClip1);
1000 }
1001 return {};
1002}
1003
1004const WaveClip* WaveTrack::FindClipByName(const wxString& name) const
1005{
1006 for (const auto& clip : mClips)
1007 {
1008 if (clip->GetName() == name)
1009 return clip.get();
1010 }
1011 return nullptr;
1012}
1013
1014std::shared_ptr<::Channel> WaveTrack::DoGetChannel(size_t iChannel)
1015{
1016 auto nChannels = NChannels();
1017 if (iChannel >= nChannels)
1018 return {};
1019 auto pTrack = (iChannel == 0)
1020 ? this
1021 // TODO: more-than-two-channels
1022 : *TrackList::Channels(this).rbegin();
1023 // Use aliasing constructor of std::shared_ptr
1024 ::Channel *alias = pTrack;
1025 return { pTrack->shared_from_this(), alias };
1026}
1027
1029{
1030 const ChannelGroup &group = *this;
1031 return const_cast<ChannelGroup&>(group);
1032}
1033
1035{
1036 const Track *pTrack = this;
1037 if (const auto pOwner = GetHolder())
1038 pTrack = *pOwner->Find(this);
1039 const ChannelGroup &group = *pTrack;
1040 return const_cast<ChannelGroup&>(group);
1041}
1042
1044{
1045 assert(IsLeader());
1046 auto result = TrackList::Temporary(nullptr);
1047 const auto cloneOne = [&](const WaveTrack *pChannel){
1048 const auto pTrack =
1049 std::make_shared<WaveTrack>(*pChannel, ProtectedCreationArg{});
1050 pTrack->Init(*pChannel);
1051 result->Add(pTrack);
1052 };
1053 if (GetOwner())
1054 for (const auto pChannel : TrackList::Channels(this))
1055 cloneOne(pChannel);
1056 else
1057 cloneOne(this);
1058 return result;
1059}
1060
1061wxString WaveTrack::MakeClipCopyName(const wxString& originalName) const
1062{
1063 auto name = originalName;
1064 for (auto i = 1;; ++i)
1065 {
1066 if (FindClipByName(name) == nullptr)
1067 return name;
1068 //i18n-hint Template for clip name generation on copy-paste
1069 name = XC("%s.%i", "clip name template").Format(originalName, i).Translation();
1070 }
1071}
1072
1074{
1075 auto name = GetName();
1076 for (auto i = 1;; ++i)
1077 {
1078 if (FindClipByName(name) == nullptr)
1079 return name;
1080 //i18n-hint Template for clip name generation on inserting new empty clip
1081 name = XC("%s %i", "clip name template").Format(GetName(), i).Translation();
1082 }
1083}
1084
1086{
1087 return WaveTrackData::Get(*this).GetRate();
1088}
1089
1090void WaveTrack::SetRate(double newRate)
1091{
1092 assert( newRate > 0 );
1093 newRate = std::max( 1.0, newRate );
1094 DoSetRate(newRate);
1095
1096 for(const auto& channel : Channels())
1097 channel->GetTrack().SetClipRates(newRate);
1098}
1099
1100void WaveTrack::DoSetRate(double newRate)
1101{
1102 auto &data = WaveTrackData::Get(*this);
1103 data.SetRate(static_cast<int>(newRate));
1104}
1105
1106void WaveTrack::SetClipRates(double newRate)
1107{
1108 for (const auto &clip : mClips)
1109 clip->SetRate(static_cast<int>(newRate));
1110}
1111
1113{
1114 return WaveTrackData::Get(*this).GetGain();
1115}
1116
1117void WaveTrack::DoSetGain(float value)
1118{
1119 WaveTrackData::Get(*this).SetGain(value);
1120}
1121
1122void WaveTrack::SetGain(float newGain)
1123{
1124 if (GetGain() != newGain) {
1125 DoSetGain(newGain);
1126 Notify(true);
1127 }
1128}
1129
1131{
1132 return WaveTrackData::Get(*this).GetPan();
1133}
1134
1135void WaveTrack::DoSetPan(float value)
1136{
1137 WaveTrackData::Get(*this).SetPan(value);
1138}
1139
1140void WaveTrack::SetPan(float newPan)
1141{
1142 if (newPan > 1.0)
1143 newPan = 1.0;
1144 else if (newPan < -1.0)
1145 newPan = -1.0;
1146
1147 if ( GetPan() != newPan ) {
1148 DoSetPan(newPan);
1149 Notify(true);
1150 }
1151}
1152
1153float WaveTrack::GetChannelGain(int channel) const
1154{
1155 float left = 1.0;
1156 float right = 1.0;
1157
1158 const auto pan = GetPan();
1159 if (pan < 0)
1160 right = (pan + 1.0);
1161 else if (pan > 0)
1162 left = 1.0 - pan;
1163
1164 const auto gain = GetGain();
1165 if ((channel%2) == 0)
1166 return left * gain;
1167 else
1168 return right * gain;
1169}
1170
1172{
1173 return WaveTrackData::Get(*this).GetWaveColorIndex();
1174}
1175
1178{
1179 assert(IsLeader());
1180 for (const auto pChannel : TrackList::Channels(this)) {
1181 for (const auto &clip : pChannel->mClips)
1182 clip->SetColourIndex(colorIndex);
1183 }
1184 WaveTrackData::Get(*this).SetWaveColorIndex(colorIndex);
1185}
1186
1188{
1189 sampleCount result{ 0 };
1190
1191 for (const auto& clip : mClips)
1192 result += clip->GetVisibleSampleCount();
1193
1194 return result;
1195}
1196
1198{
1199 assert(IsLeader());
1200 sampleCount result{ 0 };
1201
1202 for (const auto pChannel : TrackList::Channels(this))
1203 for (const auto& clip : pChannel->mClips)
1204 result += clip->GetSequenceSamplesCount();
1205
1206 return result;
1207}
1208
1210{
1211 assert(IsLeader());
1212 size_t result{};
1213 for (const auto pChannel : TrackList::Channels(this)) {
1214 for (auto& clip : pChannel->GetClips())
1215 result += clip->GetWidth() * clip->GetSequenceBlockArray(0)->size();
1216 }
1217 return result;
1218}
1219
1221{
1222 return WaveTrackData::Get(*this).GetSampleFormat();
1223}
1224
1227 const std::function<void(size_t)> & progressReport)
1228{
1229 assert(IsLeader());
1230 for (const auto pChannel : TrackList::Channels(this)) {
1231 for (const auto& clip : pChannel->mClips)
1232 clip->ConvertToSampleFormat(format, progressReport);
1233 }
1234 WaveTrackData::Get(*this).SetSampleFormat(format);
1235}
1236
1237
1238bool WaveTrack::IsEmpty(double t0, double t1) const
1239{
1240 if (t0 > t1)
1241 return true;
1242
1243 //wxPrintf("Searching for overlap in %.6f...%.6f\n", t0, t1);
1244 for (const auto &clip : mClips)
1245 {
1246 if (clip->IntersectsPlayRegion(t0, t1)) {
1247 //wxPrintf("Overlapping clip: %.6f...%.6f\n",
1248 // clip->GetStartTime(),
1249 // clip->GetEndTime());
1250 // We found a clip that overlaps this region
1251 return false;
1252 }
1253 }
1254 //wxPrintf("No overlap found\n");
1255
1256 // Otherwise, no clips overlap this region
1257 return true;
1258}
1259
1260TrackListHolder WaveTrack::Cut(double t0, double t1)
1261{
1262 assert(IsLeader());
1263 if (t1 < t0)
1265
1266 auto result = Copy(t0, t1);
1267 Clear(t0, t1);
1268 return result;
1269}
1270
1273{
1274 assert(IsLeader());
1275 if (t1 < t0)
1277
1278 // SplitCut is the same as 'Copy', then 'SplitDelete'
1279 auto result = Copy(t0, t1);
1280 SplitDelete(t0, t1);
1281 return result;
1282}
1283
1284//Trim trims within a clip, rather than trimming everything.
1285//If a bound is outside a clip, it trims everything.
1287void WaveTrack::Trim (double t0, double t1)
1288{
1289 assert(IsLeader());
1290 bool inside0 = false;
1291 bool inside1 = false;
1292
1293 const auto range = TrackList::Channels(this);
1294 for (auto pChannel : range) {
1295 for (const auto &clip : pChannel->mClips) {
1296 if (t1 > clip->GetPlayStartTime() && t1 < clip->GetPlayEndTime()) {
1297 clip->SetTrimRight(
1298 clip->GetTrimRight() + clip->GetPlayEndTime() - t1);
1299 inside1 = true;
1300 }
1301
1302 if (t0 > clip->GetPlayStartTime() && t0 < clip->GetPlayEndTime()) {
1303 clip->SetTrimLeft(
1304 clip->GetTrimLeft() + t0 - clip->GetPlayStartTime());
1305 inside0 = true;
1306 }
1307 }
1308 }
1309
1310 //if inside0 is false, then the left selector was between
1311 //clips, so DELETE everything to its left.
1312 if (const auto endTime = GetEndTime()
1313 ; !inside1 && t1 < endTime
1314 )
1315 Clear(t1, endTime);
1316
1317 if (const auto startTime = GetStartTime()
1318 ; !inside0 && t0 > startTime
1319 )
1320 SplitDelete(startTime, t0);
1321}
1322
1324 const SampleBlockFactoryPtr &pFactory, bool keepLink) const
1325{
1326 const auto rate = GetRate();
1327 auto result = std::make_shared<WaveTrack>(pFactory, GetSampleFormat(), rate);
1328 result->Init(*this);
1329 // The previous line might have destroyed the rate information stored in
1330 // channel group data. The copy is not yet in a TrackList. Reassign rate
1331 // in case the track needs to make WaveClips before it is properly joined
1332 // with the opposite channel in a TrackList.
1333 // TODO wide wave tracks -- all of the comment above will be irrelevant!
1334 result->DoSetRate(rate);
1335 result->mpFactory = pFactory ? pFactory : mpFactory;
1336 if (!keepLink)
1337 result->SetLinkType(LinkType::None);
1338 WaveTrackData::Get(*result).SetOrigin(0);
1339 return result;
1340}
1341
1343 const SampleBlockFactoryPtr &pFactory, bool keepLink) const
1344{
1345 assert(IsLeader());
1346 auto result = TrackList::Temporary(nullptr);
1347 for (const auto pChannel : TrackList::Channels(this)) {
1348 const auto pNewTrack =
1349 result->Add(pChannel->EmptyCopy(pFactory, keepLink));
1350 assert(!keepLink || pNewTrack->IsLeader() == pChannel->IsLeader());
1351 }
1352 return result;
1353}
1354
1356{
1357 assert(!GetOwner());
1358
1359 auto result = Duplicate();
1360 result->MakeMultiChannelTrack(**result->begin(), 2);
1361
1362 return result;
1363}
1364
1365TrackListHolder WaveTrack::Copy(double t0, double t1, bool forClipboard) const
1366{
1367 if (t1 < t0)
1369
1370 auto list = TrackList::Create(nullptr);
1371 for (const auto pChannel : TrackList::Channels(this))
1372 list->Add(CopyOne(*pChannel, t0, t1, forClipboard));
1373 return list;
1374}
1375
1377 const WaveTrack &track, double t0, double t1, bool forClipboard) -> Holder
1378{
1379 const auto &pFactory = track.mpFactory;
1380 auto result = track.EmptyCopy();
1381 WaveTrack *newTrack = result.get();
1382
1383 // PRL: Why shouldn't cutlines be copied and pasted too? I don't know,
1384 // but that was the old behavior. But this function is also used by the
1385 // Duplicate command and I changed its behavior in that case.
1386
1387 for (const auto &clip : track.mClips) {
1388 if(clip->IsEmpty())
1389 continue;
1390
1391 if (t0 <= clip->GetPlayStartTime() && t1 >= clip->GetPlayEndTime()) {
1392 // Whole clip is in copy region
1393 //wxPrintf("copy: clip %i is in copy region\n", (int)clip);
1394
1395 newTrack->InsertClip(
1396 std::make_shared<WaveClip>(*clip, pFactory, !forClipboard));
1397 WaveClip *const newClip = newTrack->mClips.back().get();
1398 newClip->ShiftBy(-t0);
1399 }
1400 else if (clip->CountSamples(t0, t1) >= 1) {
1401 // Clip is affected by command
1402 //wxPrintf("copy: clip %i is affected by command\n", (int)clip);
1403
1404 auto newClip = std::make_shared<WaveClip>(
1405 *clip, pFactory, !forClipboard, t0, t1);
1406 newClip->SetName(clip->GetName());
1407
1408 newClip->ShiftBy(-t0);
1409 if (newClip->GetPlayStartTime() < 0)
1410 newClip->SetPlayStartTime(0);
1411
1412 newTrack->InsertClip(std::move(newClip)); // transfer ownership
1413 }
1414 }
1415
1416 // AWD, Oct 2009: If the selection ends in whitespace, create a
1417 // placeholder clip representing that whitespace
1418 // PRL: Only if we want the track for pasting into other tracks. Not if
1419 // it goes directly into a project as in the Duplicate command.
1420 if (forClipboard &&
1421 newTrack->GetEndTime() + 1.0 / newTrack->GetRate() < t1 - t0) {
1422 // TODO wide wave tracks -- match clip width of newTrack
1423 auto placeholder = std::make_shared<WaveClip>(1, pFactory,
1424 newTrack->GetSampleFormat(),
1425 static_cast<int>(newTrack->GetRate()),
1426 0 /*colourindex*/);
1427 placeholder->SetIsPlaceholder(true);
1428 placeholder->InsertSilence(0, (t1 - t0) - newTrack->GetEndTime());
1429 placeholder->ShiftBy(newTrack->GetEndTime());
1430 newTrack->InsertClip(std::move(placeholder)); // transfer ownership
1431 }
1432 return newTrack->SharedPointer<WaveTrack>();
1433}
1434
1436void WaveTrack::Clear(double t0, double t1)
1437{
1438 assert(IsLeader());
1439 for (const auto pChannel : TrackList::Channels(this))
1440 pChannel->HandleClear(t0, t1, false, false);
1441}
1442
1444void WaveTrack::ClearAndAddCutLine(double t0, double t1)
1445{
1446 assert(IsLeader());
1447 for (const auto pChannel : TrackList::Channels(this))
1448 pChannel->HandleClear(t0, t1, true, false);
1449}
1450
1451namespace {
1452
1453 //Internal structure, which is supposed to contain
1454 //data related to clip boundaries, and used during
1455 //ClearAndPaste to restore old splits after new
1456 //data is pasted
1458 {
1459 //Time, where boundary is located
1460 double time;
1461 //Contains trimmed data, which should be re-appended to
1462 //the clip to the left from the boundary, may be null
1463 std::shared_ptr<WaveClip> left;
1464 //Contains trimmed data, which should be re-appended to
1465 //the clip to the right from the boundary, may be null
1466 std::shared_ptr<WaveClip> right;
1467 //Contains clip name next to the left from the boundary,
1468 //if present, that needs to be re-assigned to the matching
1469 //clip after split
1470 std::optional<wxString> leftClipName;
1471 //Contains clip name next to the right from the boundary,
1472 //if present, that needs to be re-assigned to the matching
1473 //clip after split
1474 std::optional<wxString> rightClipName;
1475 };
1476
1477}
1478
1479//
1480// ClearAndPaste() is a specialized version of HandleClear()
1481// followed by Paste() and is used mostly by effects that
1482// can't replace track data directly using Get()/Set().
1483//
1484// HandleClear() removes any cut/split lines with the
1485// cleared range, but, in most cases, effects want to preserve
1486// the existing cut/split lines, trimmed data and clip names,
1487// so they are saved before the HandleClear()/Paste() and restored after.
1488// When pasted track has split lines with hidden data at same places
1489// as the target one, then only targets hidden data is preserved, and
1490// hidden data from pasted track is discarded.
1491//
1492// If the pasted track overlaps two or more clips, then it will
1493// be pasted with visible split lines. Normally, effects do not
1494// want these extra lines, so they may be merged out.
1495//
1499 double t0, // Start of time to clear
1500 double t1, // End of time to clear
1501 const WaveTrack& src, // What to paste
1502 bool preserve, // Whether to reinsert splits/cuts
1503 bool merge, // Whether to remove 'extra' splits
1504 const TimeWarper* effectWarper, // How does time change
1505 bool clearByTrimming)
1506{
1507 // Get a modifiable copy of `src` because it may come from another project
1508 // with different tempo, making boundary queries incorrect.
1509 const auto& tempo = GetProjectTempo();
1510 if (!tempo.has_value())
1512 WaveTrack* copy;
1513 const auto copyHolder = src.DuplicateWithOtherTempo(*tempo, copy);
1515 t0, t1, *copy, preserve, merge, effectWarper, clearByTrimming);
1516}
1517
1519 double t0, double t1, const WaveTrack& src, bool preserve, bool merge,
1520 const TimeWarper* effectWarper, bool clearByTrimming)
1521{
1522 const auto srcNChannels = src.NChannels();
1523 assert(IsLeader());
1524 assert(src.IsLeader());
1525 assert(srcNChannels == 1 || srcNChannels == NChannels());
1526 assert(
1527 GetProjectTempo().has_value() &&
1528 GetProjectTempo() == src.GetProjectTempo());
1529
1530 t0 = SnapToSample(t0);
1531 t1 = SnapToSample(t1);
1532
1533 const auto startTime = src.GetStartTime();
1534 const auto endTime = src.GetEndTime();
1535 double dur = std::min(t1 - t0, endTime);
1536
1537 // If duration is 0, then it's just a plain paste
1538 if (dur == 0.0) {
1539 // use Weak-guarantee
1540 PasteWaveTrack(t0, src, merge);
1541 return;
1542 }
1543
1544 auto iter = TrackList::Channels(&src).begin();
1545 const auto myChannels = TrackList::Channels(this);
1546 for (const auto pChannel : myChannels) {
1548 *pChannel, t0, t1, startTime, endTime, **iter, preserve, merge,
1549 effectWarper, clearByTrimming);
1550 if (srcNChannels > 1)
1551 ++iter;
1552 }
1553}
1554
1556 WaveTrack& track, double t0, double t1, const double startTime,
1557 const double endTime, const WaveTrack& src, bool preserve, bool merge,
1558 const TimeWarper* effectWarper, bool clearByTrimming)
1559{
1560 const auto pFactory = track.mpFactory;
1561
1562 std::vector<SplitInfo> splits;
1563 WaveClipHolders cuts;
1564
1565 //helper routine, that finds SplitInfo by time value,
1566 //or creates a new one if no one exists yet
1567 auto get_split = [&](double time) {
1568 auto it = std::find_if(splits.begin(), splits.end(),
1569 [time](const SplitInfo& split) { return split.time == time; });
1570 if(it == splits.end())
1571 it = splits.insert(
1572 splits.end(),
1573 { time, nullptr, nullptr, std::nullopt, std::nullopt }
1574 );
1575 return it;
1576 };
1577
1578 // If provided time warper was NULL, use a default one that does nothing
1579 IdentityTimeWarper localWarper;
1580 const TimeWarper *warper = (effectWarper ? effectWarper : &localWarper);
1581
1582 const auto roundTime = [&track](double t){
1583 return track.SnapToSample(t);
1584 };
1585
1586 // Align to a sample
1587 t0 = roundTime(t0);
1588 t1 = roundTime(t1);
1589
1590 // Save the cut/split lines whether preserving or not since merging
1591 // needs to know if a clip boundary is being crossed since Paste()
1592 // will add split lines around the pasted clip if so.
1593 for (const auto &clip : track.mClips) {
1594 double st;
1595
1596 // Remember clip boundaries as locations to split
1597 // we need to copy clips, trims and names, because the original ones
1598 // could be changed later during Clear/Paste routines
1599 st = roundTime(clip->GetPlayStartTime());
1600 if (st >= t0 && st <= t1) {
1601 auto it = get_split(st);
1602 if (clip->GetTrimLeft() != 0) {
1603 //keep only hidden left part
1604 it->right = std::make_shared<WaveClip>(*clip, pFactory, false);
1605 it->right->SetTrimLeft(.0);
1606 it->right->ClearRight(clip->GetPlayStartTime());
1607 }
1608 it->rightClipName = clip->GetName();
1609 }
1610
1611 st = roundTime(clip->GetPlayEndTime());
1612 if (st >= t0 && st <= t1) {
1613 auto it = get_split(st);
1614 if (clip->GetTrimRight() != 0) {
1615 //keep only hidden right part
1616 it->left = std::make_shared<WaveClip>(*clip, pFactory, false);
1617 it->left->SetTrimRight(.0);
1618 it->left->ClearLeft(clip->GetPlayEndTime());
1619 }
1620 it->leftClipName = clip->GetName();
1621 }
1622
1623 // Search for cut lines
1624 auto &cutlines = clip->GetCutLines();
1625 // May erase from cutlines, so don't use range-for
1626 for (auto it = cutlines.begin(); it != cutlines.end(); ) {
1627 WaveClip *cut = it->get();
1628 const double cs = roundTime(
1629 clip->GetSequenceStartTime() + cut->GetSequenceStartTime());
1630
1631 // Remember cut point
1632 if (cs >= t0 && cs <= t1) {
1633 // Remember the absolute offset and add to our cuts array.
1634 cut->SetSequenceStartTime(cs);
1635 cuts.push_back(std::move(*it)); // transfer ownership!
1636 it = cutlines.erase(it);
1637 }
1638 else
1639 ++it;
1640 }
1641 }
1642
1643 const auto tolerance = 2.0 / track.GetRate();
1644
1645 // This is not a split-cut operation.
1646 constexpr auto split = false;
1647
1648 // Now, clear the selection
1649 track.HandleClear(t0, t1, false, split, clearByTrimming);
1650
1651 // And paste in the new data
1652 PasteOne(track, t0, src, startTime, endTime, merge);
1653
1654 // First, merge the new clip(s) in with the existing clips
1655 if (merge && splits.size() > 0) {
1656 {
1657 // Now t1 represents the absolute end of the pasted data.
1658 t1 = t0 + endTime;
1659
1660 // Get a sorted array of the clips
1661 auto clips = track.SortedClipArray();
1662
1663 // Scan the sorted clips for the first clip whose start time
1664 // exceeds the pasted regions end time.
1665 {
1666 WaveClip *prev = nullptr;
1667 for (const auto clip : clips) {
1668 // Merge this clip and the previous clip if the end time
1669 // falls within it and this isn't the first clip in the track.
1670 if (fabs(t1 - clip->GetPlayStartTime()) < tolerance) {
1671 if (prev && clip->HasEqualStretchRatio(*prev))
1672 track.MergeOneClipPair(track.GetClipIndex(prev),
1673 track.GetClipIndex(clip));
1674 break;
1675 }
1676 prev = clip;
1677 }
1678 }
1679 }
1680
1681 {
1682 // Refill the array since clips have changed.
1683 auto clips = track.SortedClipArray();
1684
1685 // Scan the sorted clips to look for the start of the pasted
1686 // region.
1687 WaveClip *prev = nullptr;
1688 for (const auto clip : clips) {
1689 if (prev) {
1690 // It must be that clip is what was pasted and it begins where
1691 // prev ends.
1692 // use Weak-guarantee
1693 if (clip->HasEqualStretchRatio(*prev))
1694 track.MergeOneClipPair(
1695 track.GetClipIndex(prev), track.GetClipIndex(clip));
1696 break;
1697 }
1698 if (fabs(t0 - clip->GetPlayEndTime()) < tolerance)
1699 // Merge this clip and the next clip if the start time
1700 // falls within it and this isn't the last clip in the track.
1701 prev = clip;
1702 else
1703 prev = nullptr;
1704 }
1705 }
1706 }
1707
1708 // Restore cut/split lines
1709 if (preserve) {
1710 auto attachLeft = [](WaveClip& target, WaveClip& src) {
1711 // What this lambda does is restoring the left hidden data of `target`
1712 // that was cleared by `HandleClear`. Hence, `target` has no left
1713 // hidden data at this stage.
1714 assert(target.GetTrimLeft() == 0);
1715 if (target.GetTrimLeft() != 0)
1716 return;
1717
1718 // `src` was created by copy from `target`, so they have equal width
1719 // and stretch ratio.
1720 assert(target.GetWidth() == src.GetWidth());
1721 assert(target.GetStretchRatio() == src.GetStretchRatio());
1722
1723 auto trim = src.GetPlayEndTime() - src.GetPlayStartTime();
1724 auto success = target.Paste(target.GetPlayStartTime(), src);
1725 assert(success); // because of precondition above
1726 target.SetTrimLeft(trim);
1727 //Play start time needs to be adjusted after
1728 //prepending data to the sequence
1729 target.ShiftBy(-trim);
1730 };
1731
1732 auto attachRight = [](WaveClip &target, WaveClip &src)
1733 {
1734 // See `attachLeft` for rationale behind these asserts.
1735 assert(target.GetTrimRight() == 0);
1736 if (target.GetTrimRight() != 0)
1737 return;
1738 assert(target.GetWidth() == src.GetWidth());
1739 assert(target.GetStretchRatio() == src.GetStretchRatio());
1740
1741 auto trim = src.GetPlayEndTime() - src.GetPlayStartTime();
1742 auto success = target.Paste(target.GetPlayEndTime(), src);
1743 assert(success); // because of precondition above
1744 target.SetTrimRight(trim);
1745 };
1746
1747 // Restore the split lines and trims, transforming the position appropriately
1748 for (const auto& split: splits) {
1749 auto at = roundTime(warper->Warp(split.time));
1750 for (const auto& clip : track.GetClips()) {
1751 // Clips in split began as copies of a clip in the track,
1752 // therefore have the same width, satisfying preconditions to
1753 // attach
1754 if (clip->SplitsPlayRegion(at))//strictly inside
1755 {
1756 auto newClip =
1757 std::make_shared<WaveClip>(*clip, pFactory, true);
1758
1759 clip->ClearRight(at);
1760 newClip->ClearLeft(at);
1761 if (split.left)
1762 // clip was cleared right
1763 attachRight(*clip, *split.left);
1764 if (split.right)
1765 // new clip was cleared left
1766 attachLeft(*newClip, *split.right);
1767 bool success = track.AddClip(std::move(newClip));
1768 assert(success); // copied clip has same width and factory
1769 break;
1770 }
1771 else if (clip->GetPlayStartSample() ==
1772 track.TimeToLongSamples(at) && split.right) {
1773 // Satisfy the precondition of attachLeft first!
1774 const auto trim = clip->GetTrimLeft();
1775 const auto seqStartTime = clip->GetSequenceStartTime();
1776 clip->Clear(seqStartTime, seqStartTime + trim);
1777 // This clearing, although only removing the hidden part, moved
1778 // the clip leftwards. We don't want this in this case.
1779 clip->ShiftBy(trim);
1780 attachLeft(*clip, *split.right);
1781 break;
1782 }
1783 else if (clip->GetPlayEndSample() ==
1784 track.TimeToLongSamples(at) && split.left) {
1785 // Satisfy the precondition of attachRight first!
1786 clip->Clear(
1787 clip->GetPlayEndTime(), clip->GetSequenceEndTime());
1788 attachRight(*clip, *split.left);
1789 break;
1790 }
1791 }
1792 }
1793
1794 //Restore clip names
1795 for (const auto& split : splits)
1796 {
1797 auto s = track.TimeToLongSamples(warper->Warp(split.time));
1798 for (auto& clip : track.GetClips()) {
1799 if (split.rightClipName.has_value() && clip->GetPlayStartSample() == s)
1800 clip->SetName(*split.rightClipName);
1801 else if (split.leftClipName.has_value() && clip->GetPlayEndSample() == s)
1802 clip->SetName(*split.leftClipName);
1803 }
1804 }
1805
1806 // Restore the saved cut lines, also transforming if time altered
1807 for (const auto &clip : track.mClips) {
1808 double st;
1809 double et;
1810
1811 st = clip->GetPlayStartTime();
1812 et = clip->GetPlayEndTime();
1813
1814 // Scan the cuts for any that live within this clip
1815 for (auto it = cuts.begin(); it != cuts.end();) {
1816 WaveClip *cut = it->get();
1817 //cutlines in this array were orphaned previously
1818 double cs = cut->GetSequenceStartTime();
1819
1820 // Offset the cut from the start of the clip and add it to
1821 // this clips cutlines.
1822 if (cs >= st && cs <= et) {
1823 cut->SetSequenceStartTime(warper->Warp(cs) - st);
1824 clip->GetCutLines().push_back( std::move(*it) ); // transfer ownership!
1825 it = cuts.erase(it);
1826 }
1827 else
1828 ++it;
1829 }
1830 }
1831 }
1832}
1833
1835void WaveTrack::SplitDelete(double t0, double t1)
1836{
1837 assert(IsLeader());
1838 bool addCutLines = false;
1839 bool split = true;
1840 for (const auto pChannel : TrackList::Channels(this))
1841 pChannel->HandleClear(t0, t1, addCutLines, split);
1842}
1843
1844namespace
1845{
1846 WaveClipHolders::const_iterator
1847 FindClip(const WaveClipHolders &list, const WaveClip *clip, int *distance = nullptr)
1848 {
1849 if (distance)
1850 *distance = 0;
1851 auto it = list.begin();
1852 for (const auto end = list.end(); it != end; ++it)
1853 {
1854 if (it->get() == clip)
1855 break;
1856 if (distance)
1857 ++*distance;
1858 }
1859 return it;
1860 }
1861
1862 WaveClipHolders::iterator
1863 FindClip(WaveClipHolders &list, const WaveClip *clip, int *distance = nullptr)
1864 {
1865 if (distance)
1866 *distance = 0;
1867 auto it = list.begin();
1868 for (const auto end = list.end(); it != end; ++it)
1869 {
1870 if (it->get() == clip)
1871 break;
1872 if (distance)
1873 ++*distance;
1874 }
1875 return it;
1876 }
1877}
1878
1879std::shared_ptr<WaveClip> WaveTrack::RemoveAndReturnClip(WaveClip* clip)
1880{
1881 // Be clear about who owns the clip!!
1882 auto it = FindClip(mClips, clip);
1883 if (it != mClips.end()) {
1884 auto result = std::move(*it); // Array stops owning the clip, before we shrink it
1885 mClips.erase(it);
1886 return result;
1887 }
1888 else
1889 return {};
1890}
1891
1892bool WaveTrack::AddClip(const std::shared_ptr<WaveClip> &clip)
1893{
1894 assert(clip);
1895 if (clip->GetSequence(0)->GetFactory() != this->mpFactory)
1896 return false;
1897
1898 if (clip->GetWidth() != GetWidth())
1899 return false;
1900
1901 // Uncomment the following line after we correct the problem of zero-length clips
1902 //if (CanInsertClip(clip))
1903 InsertClip(clip); // transfer ownership
1904
1905 return true;
1906}
1907
1910 double t0, double t1, bool addCutLines, bool split, bool clearByTrimming)
1911{
1912 // For debugging, use an ASSERT so that we stop
1913 // closer to the problem.
1914 wxASSERT( t1 >= t0 );
1915 if (t1 < t0)
1917
1918 t0 = SnapToSample(t0);
1919 t1 = SnapToSample(t1);
1920
1921 WaveClipPointers clipsToDelete;
1922 WaveClipHolders clipsToAdd;
1923
1924 // We only add cut lines when deleting in the middle of a single clip
1925 // The cut line code is not really prepared to handle other situations
1926 if (addCutLines)
1927 {
1928 for (const auto &clip : mClips)
1929 {
1930 if (clip->PartlyWithinPlayRegion(t0, t1))
1931 {
1932 addCutLines = false;
1933 break;
1934 }
1935 }
1936 }
1937
1938 for (const auto &clip : mClips)
1939 {
1940 if (clip->CoversEntirePlayRegion(t0, t1))
1941 {
1942 // Whole clip must be deleted - remember this
1943 clipsToDelete.push_back(clip.get());
1944 }
1945 else if (clip->IntersectsPlayRegion(t0, t1))
1946 {
1947 // Clip data is affected by command
1948 if (addCutLines)
1949 {
1950 // Don't modify this clip in place, because we want a strong
1951 // guarantee, and might modify another clip
1952 clipsToDelete.push_back( clip.get() );
1953 auto newClip =
1954 std::make_shared<WaveClip>(*clip, mpFactory, true);
1955 newClip->ClearAndAddCutLine( t0, t1 );
1956 clipsToAdd.push_back( std::move( newClip ) );
1957 }
1958 else
1959 {
1960 if (split || clearByTrimming) {
1961 // Three cases:
1962
1963 if (clip->BeforePlayRegion(t0)) {
1964 // Delete from the left edge
1965
1966 // Don't modify this clip in place, because we want a strong
1967 // guarantee, and might modify another clip
1968 clipsToDelete.push_back( clip.get() );
1969 auto newClip =
1970 std::make_shared<WaveClip>(*clip, mpFactory, true);
1971 newClip->TrimLeft(t1 - clip->GetPlayStartTime());
1972 if (!split)
1973 // If this is not a split-cut, where things are left in
1974 // place, we need to reposition the clip.
1975 newClip->ShiftBy(t0 - t1);
1976 clipsToAdd.push_back( std::move( newClip ) );
1977 }
1978 else if (clip->AfterPlayRegion(t1)) {
1979 // Delete to right edge
1980
1981 // Don't modify this clip in place, because we want a strong
1982 // guarantee, and might modify another clip
1983 clipsToDelete.push_back( clip.get() );
1984 auto newClip =
1985 std::make_shared<WaveClip>(*clip, mpFactory, true);
1986 newClip->TrimRight(clip->GetPlayEndTime() - t0);
1987
1988 clipsToAdd.push_back( std::move( newClip ) );
1989 }
1990 else {
1991 // Delete in the middle of the clip...we actually create two
1992 // NEW clips out of the left and right halves...
1993
1994 auto leftClip =
1995 std::make_shared<WaveClip>(*clip, mpFactory, true);
1996 leftClip->TrimRight(clip->GetPlayEndTime() - t0);
1997 clipsToAdd.push_back(std::move(leftClip));
1998
1999 auto rightClip =
2000 std::make_shared<WaveClip>(*clip, mpFactory, true);
2001 rightClip->TrimLeft(t1 - clip->GetPlayStartTime());
2002 if (!split)
2003 // If this is not a split-cut, where things are left in
2004 // place, we need to reposition the clip.
2005 rightClip->ShiftBy(t0 - t1);
2006 clipsToAdd.push_back(std::move(rightClip));
2007
2008 clipsToDelete.push_back(clip.get());
2009 }
2010 }
2011 else {
2012 // (We are not doing a split cut)
2013
2014 // Don't modify this clip in place, because we want a strong
2015 // guarantee, and might modify another clip
2016 clipsToDelete.push_back( clip.get() );
2017 auto newClip =
2018 std::make_shared<WaveClip>(*clip, mpFactory, true);
2019
2020 // clip->Clear keeps points < t0 and >= t1 via Envelope::CollapseRegion
2021 newClip->Clear(t0,t1);
2022
2023 clipsToAdd.push_back( std::move( newClip ) );
2024 }
2025 }
2026 }
2027 }
2028
2029 // Only now, change the contents of this track
2030 // use No-fail-guarantee for the rest
2031
2032 const auto moveClipsLeft = !split && GetEditClipsCanMove();
2033 if (moveClipsLeft)
2034 // Clip is "behind" the region -- offset it unless we're splitting
2035 // or we're using the "don't move other clips" mode
2036 for (const auto& clip : mClips)
2037 if (clip->AtOrBeforePlayRegion(t1))
2038 clip->ShiftBy(-(t1 - t0));
2039
2040 for (const auto &clip: clipsToDelete)
2041 {
2042 auto myIt = FindClip(mClips, clip);
2043 if (myIt != mClips.end())
2044 mClips.erase(myIt); // deletes the clip!
2045 else
2046 wxASSERT(false);
2047 }
2048
2049 for (auto &clip: clipsToAdd)
2050 InsertClip(std::move(clip)); // transfer ownership
2051}
2052
2053void WaveTrack::SyncLockAdjust(double oldT1, double newT1)
2054{
2055 assert(IsLeader());
2056 const auto endTime = GetEndTime();
2057 if (newT1 > oldT1 &&
2058 // JKC: This is a rare case where using >= rather than > on a float matters.
2059 // GetEndTime() looks through the clips and may give us EXACTLY the same
2060 // value as T1, when T1 was set to be at the end of one of those clips.
2061 oldT1 >= endTime)
2062 return;
2063 const auto channels = TrackList::Channels(this);
2064 if (newT1 > oldT1) {
2065 // Insert space within the track
2066
2067 // If track is empty at oldT1 insert whitespace; otherwise, silence
2068 if (IsEmpty(oldT1, oldT1)) {
2069 // Check if clips can move
2070 if (EditClipsCanMove.Read()) {
2071 const auto offset = newT1 - oldT1;
2072 const auto rate = GetRate();
2073 for (const auto pChannel : channels)
2074 for (const auto& clip : pChannel->mClips)
2075 if (clip->GetPlayStartTime() > oldT1 - (1.0 / rate))
2076 clip->ShiftBy(offset);
2077 }
2078 return;
2079 }
2080 else {
2081 // AWD: Could just use InsertSilence() on its own here, but it doesn't
2082 // follow EditClipCanMove rules (Paste() does it right)
2083 const auto duration = newT1 - oldT1;
2084 for (const auto pChannel : channels) {
2085 auto tmp = std::make_shared<WaveTrack>(
2087 // tmpList exists only to fix assertion crashes in usage of tmp
2088 auto tmpList = TrackList::Temporary(nullptr, tmp, nullptr);
2089 assert(tmp->IsLeader()); // It is not yet owned by a TrackList
2090 tmp->InsertSilence(0.0, duration);
2091 tmp->FlushOne();
2092 PasteOne(*pChannel, oldT1, *tmp, 0.0, duration);
2093 }
2094 }
2095 }
2096 else if (newT1 < oldT1)
2097 Clear(newT1, oldT1);
2098}
2099
2100void WaveTrack::PasteWaveTrack(double t0, const WaveTrack& other, bool merge)
2101{
2102 // Get a modifiable copy of `src` because it may come from another project
2103 // with different tempo, making boundary queries incorrect.
2104 const auto& tempo = GetProjectTempo();
2105 if (!tempo.has_value())
2107 WaveTrack* copy;
2108 const auto copyHolder = other.DuplicateWithOtherTempo(*tempo, copy);
2109 PasteWaveTrackAtSameTempo(t0, *copy, merge);
2110}
2111
2113 double t0, const WaveTrack& other, bool merge)
2114{
2115 assert(IsLeader());
2116 const auto otherNChannels = other.NChannels();
2117 assert(otherNChannels == 1 || otherNChannels == NChannels());
2118 assert(
2119 GetProjectTempo().has_value() &&
2120 GetProjectTempo() == other.GetProjectTempo());
2121 const auto startTime = other.GetStartTime();
2122 const auto endTime = other.GetEndTime();
2123 auto iter = TrackList::Channels(&other).begin();
2124 for (const auto pChannel : TrackList::Channels(this)) {
2125 PasteOne(*pChannel, t0, **iter, startTime, endTime, merge);
2126 if (otherNChannels > 1)
2127 ++iter;
2128 }
2129}
2130
2132 WaveTrack& track, double t0, const WaveTrack& other, const double startTime,
2133 const double insertDuration, bool merge)
2134{
2135 //
2136 // Pasting is a bit complicated, because with the existence of multiclip mode,
2137 // we must guess the behaviour the user wants.
2138 //
2139 // Currently, two modes are implemented:
2140 //
2141 // - If a single clip should be pasted, and it should be pasted inside another
2142 // clip, no NEW clips are generated. The audio is simply inserted.
2143 // This resembles the old (pre-multiclip support) behaviour. However, if
2144 // the clip is pasted outside of any clip, a NEW clip is generated. This is
2145 // the only behaviour which is different to what was done before, but it
2146 // shouldn't confuse users too much.
2147 //
2148 // - If multiple clips should be pasted, or a single clip that does not fill
2149 // the duration of the pasted track, these are always pasted as single
2150 // clips, and the current clip is split, when necessary. This may seem
2151 // strange at first, but it probably is better than trying to auto-merge
2152 // anything. The user can still merge the clips by hand (which should be a
2153 // simple command reachable by a hotkey or single mouse click).
2154 //
2155
2156 if (other.GetNumClips() == 0)
2157 return;
2158
2159 t0 = track.SnapToSample(t0);
2160
2161 //wxPrintf("paste: we have at least one clip\n");
2162
2163 const auto clipAtT0 = track.GetClipAtTime(t0);
2164 const auto otherFirstClip = other.GetLeftmostClip();
2165 const auto otherLastClip = other.GetRightmostClip();
2166 const auto stretchRatiosMatch =
2167 !clipAtT0 || (clipAtT0->HasEqualStretchRatio(*otherFirstClip) &&
2168 clipAtT0->HasEqualStretchRatio(*otherLastClip));
2169
2170 // `singleClipMode` will try to merge. Only allow this if clips on both ends
2171 // of the selection have equal stretch ratio.
2172 const bool singleClipMode =
2173 other.GetNumClips() == 1 &&
2174 std::abs(startTime) < track.LongSamplesToTime(1) * 0.5 &&
2175 stretchRatiosMatch && merge;
2176
2177 const auto rate = track.GetRate();
2178 if (insertDuration != 0 && insertDuration < 1.0 / rate)
2179 // PRL: I added this check to avoid violations of preconditions in other WaveClip and Sequence
2180 // methods, but allow the value 0 so I don't subvert the purpose of commit
2181 // 739422ba70ceb4be0bb1829b6feb0c5401de641e which causes append-recording always to make
2182 // a new clip.
2183 return;
2184
2185 //wxPrintf("Check if we need to make room for the pasted data\n");
2186
2187 auto pastingFromTempTrack = !other.GetOwner();
2188 bool editClipCanMove = GetEditClipsCanMove();
2189
2190 const SimpleMessageBoxException notEnoughSpaceException {
2192 XO("There is not enough room available to paste the selection"),
2193 XO("Warning"), "Error:_Insufficient_space_in_track"
2194 };
2195
2196 // Make room for the pasted data
2197 if (editClipCanMove) {
2198 if (!singleClipMode) {
2199 // We need to insert multiple clips, so split the current clip and ...
2200 track.SplitAt(t0);
2201 }
2202 //else if there is a clip at t0 insert new clip inside it and ...
2203
2204 // ... move everything to the right
2205 for (const auto& clip : track.mClips)
2206 if (clip->GetPlayStartTime() > t0 - (1.0 / rate))
2207 clip->ShiftBy(insertDuration);
2208 }
2209 else
2210 {
2211 if (!merge)
2212 track.SplitAt(t0);
2213 const auto clipAtT0 = track.GetClipAtTime(t0);
2214 const auto t = clipAtT0 ? clipAtT0->GetPlayEndTime() : t0;
2215 if (!track.IsEmpty(t, t + insertDuration))
2216 throw notEnoughSpaceException;
2217 }
2218
2219 // See if the clipboard data is one clip only and if it should be merged. If
2220 // edit-clip-can-move mode is checked, merging happens only if the pasting
2221 // point splits a clip. If it isn't, merging also happens when the pasting
2222 // point is at the exact beginning of a clip.
2223 if (singleClipMode && merge) {
2224 // Single clip mode
2225 // wxPrintf("paste: checking for single clip mode!\n");
2226
2227 WaveClip* insideClip = nullptr;
2228 for (const auto& clip : track.mClips) {
2229 if (editClipCanMove) {
2230 if (clip->SplitsPlayRegion(t0)) {
2231 //wxPrintf("t0=%.6f: inside clip is %.6f ... %.6f\n",
2232 // t0, clip->GetStartTime(), clip->GetEndTime());
2233 insideClip = clip.get();
2234 break;
2235 }
2236 }
2237 else {
2238 // If clips are immovable we also allow prepending to clips
2239 if (clip->WithinPlayRegion(t0))
2240 {
2241 insideClip = clip.get();
2242 break;
2243 }
2244 }
2245 }
2246
2247 if (insideClip) {
2248 // Exhibit traditional behaviour
2249 //wxPrintf("paste: traditional behaviour\n");
2250 if (!editClipCanMove) {
2251 // We did not move other clips out of the way already, so
2252 // check if we can paste without having to move other clips
2253 for (const auto& clip : track.mClips) {
2254 if (clip->GetPlayStartTime() > insideClip->GetPlayStartTime() &&
2255 insideClip->GetPlayEndTime() + insertDuration >
2256 clip->GetPlayStartTime())
2257 // Strong-guarantee in case of this path
2258 // not that it matters.
2259 throw notEnoughSpaceException;
2260 }
2261 }
2262 if (auto *pClip = other.GetClipByIndex(0)) {
2263 // This branch only gets executed in `singleClipMode` - we've
2264 // already made sure that stretch ratios are equal, satisfying
2265 // `WaveClip::Paste`'s precondition.
2266 bool success = insideClip->Paste(t0, *pClip);
2267 // TODO wide wave tracks -- prove success, or propagate failure,
2268 // or we might throw a MessageBoxException
2269 // (which would require a change in base class Track)
2270 // for now it would be quiet failure if clip widths mismatched
2271 // Can't yet assert(success);
2272 }
2273 return;
2274 }
2275 // Just fall through and exhibit NEW behaviour
2276 }
2277
2278 // Insert NEW clips
2279 //wxPrintf("paste: multi clip mode!\n");
2280
2281 if (!editClipCanMove &&
2282 !track.IsEmpty(t0, t0 + insertDuration - 1.0 / rate))
2283 // Strong-guarantee in case of this path
2284 // not that it matters.
2285 throw notEnoughSpaceException;
2286
2287 for (const auto& clip : other.mClips) {
2288 // AWD Oct. 2009: Don't actually paste in placeholder clips
2289 if (!clip->GetIsPlaceholder()) {
2290 auto newClip =
2291 std::make_shared<WaveClip>(*clip, track.mpFactory, true);
2292 newClip->Resample(rate);
2293 newClip->ShiftBy(t0);
2294 newClip->MarkChanged();
2295 if (pastingFromTempTrack)
2296 //Clips from the tracks which aren't bound to any TrackList are
2297 //considered to be new entities, thus named using "new" name template
2298 newClip->SetName(track.MakeNewClipName());
2299 else
2300 newClip->SetName(track.MakeClipCopyName(clip->GetName()));
2301 track.InsertClip(std::move(newClip)); // transfer ownership
2302 }
2303 }
2304}
2305
2307{
2308 assert(IsLeader());
2309
2310 // The channels and all clips in them should have the same sample rate.
2311 std::optional<double> oRate;
2312 auto channels = TrackList::Channels(this);
2313 return std::all_of(channels.begin(), channels.end(),
2314 [&](const WaveTrack *pTrack){
2315 if (!pTrack)
2316 return false;
2317
2318 const auto rate = pTrack->mLegacyRate;
2319 if (!oRate)
2320 oRate = rate;
2321 else if (*oRate != rate)
2322 return false;
2323 return true;
2324 });
2325}
2326
2328{
2329 assert(IsLeader());
2330
2331 const auto channels = TrackList::Channels(this);
2332 return std::all_of(channels.begin(), channels.end(),
2333 [&](const WaveTrack *pTrack){
2334 return pTrack && pTrack->mLegacyFormat == mLegacyFormat;
2335 });
2336}
2337
2339{
2340 if(!clip->GetIsPlaceholder() && clip->IsEmpty())
2341 return false;
2342
2343 const auto& tempo = GetProjectTempo();
2344 if (tempo.has_value())
2345 clip->OnProjectTempoChange(std::nullopt, *tempo);
2346 mClips.push_back(std::move(clip));
2347
2348 return true;
2349}
2350
2352 std::optional<TimeInterval> interval, ProgressReporter reportProgress)
2353{
2354 assert(IsLeader());
2355 // Assert that the interval is reasonable, but this function will be no-op
2356 // anyway if not
2357 assert(!interval.has_value() ||
2358 interval->first <= interval->second);
2359 if (GetNumClips() == 0)
2360 return;
2361 const auto startTime =
2362 interval ? std::max(SnapToSample(interval->first), GetStartTime()) :
2363 GetStartTime();
2364 const auto endTime =
2365 interval ? std::min(SnapToSample(interval->second), GetEndTime()) :
2366 GetEndTime();
2367 if (startTime >= endTime)
2368 return;
2369
2370 // Here we assume that left- and right clips are aligned.
2371 if (auto clipAtT0 = GetClipAtTime(startTime);
2372 clipAtT0 && clipAtT0->SplitsPlayRegion(startTime) &&
2373 !clipAtT0->StretchRatioEquals(1))
2374 Split(startTime, startTime);
2375 if (auto clipAtT1 = GetClipAtTime(endTime);
2376 clipAtT1 && clipAtT1->SplitsPlayRegion(endTime) &&
2377 !clipAtT1->StretchRatioEquals(1))
2378 Split(endTime, endTime);
2379
2380 std::vector<IntervalHolder> srcIntervals;
2381 auto clip = GetIntervalAtTime(startTime);
2382 while (clip && clip->GetPlayStartTime() < endTime)
2383 {
2384 if (clip->GetStretchRatio() != 1)
2385 srcIntervals.push_back(clip);
2387 }
2388
2389 ApplyStretchRatioOnIntervals(srcIntervals, reportProgress);
2390}
2391
2393void WaveTrack::Paste(double t0, const Track &src)
2394{
2395 assert(IsLeader()); // pre of Track::Paste
2396 if (const auto other = dynamic_cast<const WaveTrack*>(&src))
2397 {
2398 // Currently `Paste` isn't used by code that wants the newer "don't merge
2399 // when copy/pasting" behaviour ...
2400 constexpr auto merge = true;
2401 PasteWaveTrack(t0, *other, merge);
2402 }
2403 else
2404 // THROW_INCONSISTENCY_EXCEPTION; // ?
2405 (void)0;// Empty if intentional.
2406}
2407
2408void WaveTrack::Silence(double t0, double t1, ProgressReporter reportProgress)
2409{
2410 assert(IsLeader());
2411 if (t1 < t0)
2413
2414 ApplyStretchRatio({ { t0, t1 } }, std::move(reportProgress));
2415
2416 auto start = TimeToLongSamples(t0);
2417 auto end = TimeToLongSamples(t1);
2418
2419 for (const auto pChannel : TrackList::Channels(this)) {
2420 for (const auto &clip : pChannel->mClips) {
2421 auto clipStart = clip->GetPlayStartSample();
2422 auto clipEnd = clip->GetPlayEndSample();
2423 if (clipEnd > start && clipStart < end) {
2424 auto offset = std::max(start - clipStart, sampleCount(0));
2425 // Clip sample region and Get/Put sample region overlap
2426 auto length = std::min(end, clipEnd) - (clipStart + offset);
2427 clip->SetSilence(offset, length);
2428 }
2429 }
2430 }
2431}
2432
2434void WaveTrack::InsertSilence(double t, double len)
2435{
2436 assert(IsLeader());
2437 // Nothing to do, if length is zero.
2438 // Fixes Bug 1626
2439 if (len == 0)
2440 return;
2441 if (len <= 0)
2443
2444 for (const auto pChannel : TrackList::Channels(this)) {
2445 auto &clips = pChannel->mClips;
2446 if (clips.empty()) {
2447 // Special case if there is no clip yet
2448 // TODO wide wave tracks -- match clip width
2449 auto clip = std::make_shared<WaveClip>(1,
2451 clip->InsertSilence(0, len);
2452 // use No-fail-guarantee
2453 pChannel->InsertClip(move(clip));
2454 }
2455 else
2456 {
2457 // Assume at most one clip contains t
2458 const auto end = clips.end();
2459 const auto it = std::find_if(clips.begin(), end,
2460 [&](const WaveClipHolder &clip) { return clip->SplitsPlayRegion(t); } );
2461
2462 // use Strong-guarantee
2463 if (it != end)
2464 it->get()->InsertSilence(t, len);
2465
2466 // use No-fail-guarantee
2467 for (const auto &clip : clips)
2468 if (clip->BeforePlayRegion(t))
2469 clip->ShiftBy(len);
2470 }
2471 }
2472}
2473
2474//Performs the opposite of Join
2475//Analyses selected region for possible Joined clips and disjoins them
2477void WaveTrack::Disjoin(double t0, double t1)
2478{
2479 assert(IsLeader());
2481 const size_t maxAtOnce = 1048576;
2482 std::vector<float> buffer;
2483 std::vector<samplePtr> buffers;
2484 Regions regions;
2485
2486 const size_t width = NChannels();
2487
2488 for (const auto &interval : Intervals()) {
2489 double startTime = interval->Start();
2490 double endTime = interval->End();
2491
2492 if (endTime < t0 || startTime > t1)
2493 continue;
2494
2495 // Assume all clips will have the same width
2496 if (buffer.empty()) {
2497 buffer.resize(maxAtOnce * width);
2498 buffers.resize(width);
2499 auto pBuffer = buffer.data();
2500 for (size_t ii = 0; ii < width; ++ii, pBuffer += maxAtOnce)
2501 buffers[ii] = reinterpret_cast<samplePtr>(pBuffer);
2502 }
2503
2504 const auto allZeroesAt = [&](size_t i) {
2505 auto pData = buffer.data() + i;
2506 for (size_t ii = 0; ii < width; ++ii, pData += maxAtOnce) {
2507 if (*pData != 0.0)
2508 return false;
2509 }
2510 return true;
2511 };
2512
2513 // simply look for a sequence of zeroes (across all channels) and if the
2514 // sequence is longer than the minimum number, split-delete the region
2515
2516 sampleCount seqStart = -1;
2517 auto start = interval->TimeToSamples(std::max(.0, t0 - startTime));
2518 auto end = interval->TimeToSamples(std::min(endTime, t1) - startTime);
2519
2520 auto len = (end - start);
2521 for (decltype(len) done = 0; done < len; done += maxAtOnce) {
2522 auto numSamples = limitSampleBufferSize(maxAtOnce, len - done);
2523
2524 auto bufferIt = buffers.begin();
2525
2526 for (auto channel : interval->Channels())
2527 channel->GetSamples(
2528 *bufferIt++, floatSample, start + done, numSamples);
2529
2530 for (decltype(numSamples) i = 0; i < numSamples; ++i) {
2531 auto curSamplePos = start + done + i;
2532
2533 //start a NEW sequence
2534 if (seqStart == -1 && allZeroesAt(i))
2535 seqStart = curSamplePos;
2536 else if (curSamplePos == end - 1 || !allZeroesAt(i)) {
2537 if (seqStart != -1) {
2538 decltype(end) seqEnd;
2539
2540 //consider the end case, where selection ends in zeroes
2541 if (curSamplePos == end - 1 && allZeroesAt(i))
2542 seqEnd = end;
2543 else
2544 seqEnd = curSamplePos;
2545 if (seqEnd - seqStart + 1 > minSamples) {
2546 regions.push_back(
2547 Region(
2548 startTime + interval->SamplesToTime(seqStart),
2549 startTime + interval->SamplesToTime(seqEnd)
2550 )
2551 );
2552 }
2553 seqStart = -1;
2554 }
2555 }
2556 } // samples
2557 } // blocks
2558 } // finding regions
2559
2560 for (const auto &region : regions)
2561 SplitDelete(region.start, region.end);
2562}
2563
2566 double t0, double t1, const ProgressReporter& reportProgress)
2567{
2568 assert(IsLeader());
2569 // Merge all WaveClips overlapping selection into one
2570 const auto intervals = Intervals();
2571 std::vector<IntervalHolder> intervalsToJoin;
2572 for (auto interval : intervals)
2573 if (interval->IntersectsPlayRegion(t0, t1))
2574 intervalsToJoin.push_back(interval);
2575 if (intervalsToJoin.size() < 2u)
2576 return;
2577 if (std::any_of(
2578 intervalsToJoin.begin() + 1, intervalsToJoin.end(),
2579 [first =
2580 intervalsToJoin[0]->GetStretchRatio()](const auto& interval) {
2581 return first != interval->GetStretchRatio();
2582 }))
2583 ApplyStretchRatioOnIntervals(intervalsToJoin, reportProgress);
2584
2585 for (const auto pChannel : TrackList::Channels(this))
2586 JoinOne(*pChannel, t0, t1);
2587}
2588
2590 WaveTrack& track, double t0, double t1)
2591{
2592 WaveClipPointers clipsToDelete;
2593 WaveClip* newClip{};
2594
2595 const auto rate = track.GetRate();
2596 auto &clips = track.mClips;
2597 for (const auto &clip: clips) {
2598 if (clip->IntersectsPlayRegion(t0, t1)) {
2599 // Put in sorted order
2600 auto it = clipsToDelete.begin(), end = clipsToDelete.end();
2601 for (; it != end; ++it)
2602 if ((*it)->GetPlayStartTime() > clip->GetPlayStartTime())
2603 break;
2604 //wxPrintf("Insert clip %.6f at position %d\n", clip->GetStartTime(), i);
2605 clipsToDelete.insert(it, clip.get());
2606 }
2607 }
2608
2609 //if there are no clips to DELETE, nothing to do
2610 if (clipsToDelete.empty())
2611 return;
2612
2613 auto t = clipsToDelete[0]->GetPlayStartTime();
2614 //preserve left trim data if any
2615 newClip = track.CreateClip(clipsToDelete[0]->GetSequenceStartTime(),
2616 clipsToDelete[0]->GetName());
2617
2618 for (auto clip : clipsToDelete) {
2619 // wxPrintf("t=%.6f adding clip (offset %.6f, %.6f ... %.6f)\n",
2620 // t, clip->GetOffset(), clip->GetStartTime(),
2621 // clip->GetEndTime());
2622
2623 if (clip->GetPlayStartTime() - t > (1.0 / rate))
2624 {
2625 double addedSilence = (clip->GetPlayStartTime() - t);
2626 // wxPrintf("Adding %.6f seconds of silence\n");
2627 auto offset = clip->GetPlayStartTime();
2628 auto value = clip->GetEnvelope()->GetValue(offset);
2629 newClip->AppendSilence(addedSilence, value);
2630 t += addedSilence;
2631 }
2632
2633 // wxPrintf("Pasting at %.6f\n", t);
2634 bool success = newClip->Paste(t, *clip);
2635 assert(success); // promise of CreateClip
2636
2637 t = newClip->GetPlayEndTime();
2638
2639 auto it = FindClip(clips, clip);
2640 clips.erase(it); // deletes the clip
2641 }
2642}
2643
2648 size_t len, unsigned stride, sampleFormat effectiveFormat)
2649{
2650 return GetTrack().Append(buffer, format, len, stride, effectiveFormat);
2651}
2652
2657 size_t len)
2658{
2659 return GetTrack().Append(buffer, format, len, 1, widestSampleFormat);
2660}
2661
2666 size_t len, unsigned int stride, sampleFormat effectiveFormat,
2667 size_t iChannel)
2668{
2669 // TODO wide wave tracks -- there will be only one clip, and its `Append`
2670 // (or an overload) must take iChannel
2671 auto pTrack = this;
2672 if (GetOwner() && iChannel == 1)
2673 pTrack = *TrackList::Channels(this).rbegin();
2674 constSamplePtr buffers[]{ buffer };
2675 return pTrack->RightmostOrNewClip()
2676 ->Append(buffers, format, len, stride, effectiveFormat);
2677}
2678
2680{
2681 auto bestBlockSize = GetMaxBlockSize();
2682
2683 for (const auto &clip : mClips)
2684 {
2685 auto startSample = clip->GetPlayStartSample();
2686 auto endSample = clip->GetPlayEndSample();
2687 if (s >= startSample && s < endSample)
2688 {
2689 // ignore extra channels (this function will soon be removed)
2690 bestBlockSize = clip->GetSequence(0)
2691 ->GetBestBlockSize(s - clip->GetSequenceStartSample());
2692 break;
2693 }
2694 }
2695
2696 return bestBlockSize;
2697}
2698
2700{
2701 decltype(GetMaxBlockSize()) maxblocksize = 0;
2702 for (const auto &clip : mClips)
2703 for (size_t ii = 0, width = clip->GetWidth(); ii < width; ++ii)
2704 maxblocksize = std::max(maxblocksize,
2705 clip->GetSequence(ii)->GetMaxBlockSize());
2706
2707 if (maxblocksize == 0)
2708 {
2709 // We really need the maximum block size, so create a
2710 // temporary sequence to get it.
2711 maxblocksize =
2713 .GetMaxBlockSize();
2714 }
2715
2716 wxASSERT(maxblocksize > 0);
2717
2718 return maxblocksize;
2719}
2720
2722{
2723 // ignore extra channels (this function will soon be removed)
2725}
2726
2727// TODO restore a proper exception safety guarantee; comment below is false
2728// because failure might happen after only one channel is done
2735{
2736 assert(IsLeader());
2737 for (const auto pChannel : TrackList::Channels(this))
2738 pChannel->FlushOne();
2739}
2740
2742{
2744}
2745
2747{
2748 const auto channels = TrackList::Channels(this);
2749 if (channels.size() != 2)
2750 return;
2751 // Assume correspondence of clips
2752 const auto left = *channels.begin();
2753 auto it = begin(left->mClips),
2754 last = end(left->mClips);
2755 const auto right = *channels.rbegin();
2756 auto it2 = begin(right->mClips),
2757 last2 = end(right->mClips);
2758 for (; it != last; ++it, ++it2) {
2759 if (it2 == last2) {
2760 assert(false);
2761 break;
2762 }
2763 (*it2)->SetEnvelope(std::make_unique<Envelope>(*(*it)->GetEnvelope()));
2764 }
2765}
2766
2773{
2774 // After appending, presumably. Do this to the clip that gets appended.
2776}
2777
2779{
2780 return Track::IsLeader();
2781}
2782
2784{
2785 return this;
2786}
2787
2789{
2790 return PlayableTrack::GetMute();
2791}
2792
2794{
2795 return PlayableTrack::GetSolo();
2796}
2797
2798bool WaveTrack::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
2799{
2800 if (tag == "wavetrack") {
2801 double dblValue;
2802 long nValue;
2803
2804 for (const auto& pair : attrs)
2805 {
2806 const auto& attr = pair.first;
2807 const auto& value = pair.second;
2808
2809 if (attr == "rate")
2810 {
2811 // mRate is an int, but "rate" in the project file is a float.
2812 if (!value.TryGet(dblValue) ||
2813 (dblValue < 1.0) || (dblValue > 1000000.0)) // allow a large range to be read
2814 return false;
2815
2816 // Defer the setting of rate until LinkConsistencyFix
2817 mLegacyRate = lrint(dblValue);
2818 }
2819 else if (attr == "offset" && value.TryGet(dblValue))
2820 {
2821 // Offset is only relevant for legacy project files. The value
2822 // is cached until the actual WaveClip containing the legacy
2823 // track is created.
2824 mLegacyProjectFileOffset = dblValue;
2825 }
2826 else if (this->WritableSampleTrack::HandleXMLAttribute(attr, value))
2827 {}
2828 else if (this->Track::HandleCommonXMLAttribute(attr, value))
2829 ;
2830 else if (attr == "gain" && value.TryGet(dblValue))
2831 DoSetGain(dblValue);
2832 else if (attr == "pan" && value.TryGet(dblValue) &&
2833 (dblValue >= -1.0) && (dblValue <= 1.0))
2834 DoSetPan(dblValue);
2835 else if (attr == "linked" && value.TryGet(nValue))
2836 SetLinkType(ToLinkType(nValue), false);
2837 else if (attr == "colorindex" && value.TryGet(nValue))
2838 // Don't use SetWaveColorIndex as it sets the clips too.
2839 WaveTrackData::Get(*this).SetWaveColorIndex(nValue);
2840 else if (attr == "sampleformat" && value.TryGet(nValue) &&
2842 {
2843 //Remember sample format until consistency check is performed.
2844 SetLegacyFormat(static_cast<sampleFormat>(nValue));
2845 }
2846 } // while
2847 return true;
2848 }
2849
2850 return false;
2851}
2852
2853void WaveTrack::HandleXMLEndTag(const std::string_view& WXUNUSED(tag))
2854{
2855#if 0
2856 // In case we opened a pre-multiclip project, we need to
2857 // simulate closing the waveclip tag.
2858 NewestOrNewClip()->HandleXMLEndTag("waveclip");
2859#else
2860 // File compatibility breaks have intervened long since, and the line above
2861 // would now have undesirable side effects
2862#endif
2863}
2864
2865XMLTagHandler *WaveTrack::HandleXMLChild(const std::string_view& tag)
2866{
2867 if ( auto pChild = WaveTrackIORegistry::Get()
2868 .CallObjectAccessor(tag, *this) )
2869 return pChild;
2870
2871 //
2872 // This is legacy code (1.2 and previous) and is not called for NEW projects!
2873 //
2874 if (tag == "sequence" || tag == "envelope")
2875 {
2876 // This is a legacy project, so set the cached offset
2878
2879 // Legacy project file tracks are imported as one single wave clip
2880 if (tag == "sequence")
2881 return NewestOrNewClip()->GetSequence(0);
2882 else if (tag == "envelope")
2883 return NewestOrNewClip()->GetEnvelope();
2884 }
2885
2886 // JKC... for 1.1.0, one step better than what we had, but still badly broken.
2887 // If we see a waveblock at this level, we'd better generate a sequence.
2888 if (tag == "waveblock")
2889 {
2890 // This is a legacy project, so set the cached offset
2892 Sequence *pSeq = NewestOrNewClip()->GetSequence(0);
2893 return pSeq;
2894 }
2895
2896 //
2897 // This is for the NEW file format (post-1.2)
2898 //
2899 if (tag == "waveclip")
2900 {
2901 // Make clips (which don't serialize the rate) consistent with channel rate,
2902 // though the consistency check of channels with each other remains to do.
2903 // Not all `WaveTrackData` fields are properly initialized by now,
2904 // use deserialization helpers.
2905 auto clip = std::make_shared<WaveClip>(1,
2907 const auto xmlHandler = clip.get();
2908 mClips.push_back(std::move(clip));
2909 return xmlHandler;
2910 }
2911
2912 return nullptr;
2913}
2914
2915void WaveTrack::WriteXML(XMLWriter &xmlFile) const
2916// may throw
2917{
2918 assert(IsLeader());
2919 const auto channels = TrackList::Channels(this);
2920 size_t iChannel = 0,
2921 nChannels = channels.size();
2922 for (const auto pChannel : channels)
2923 WriteOneXML(*pChannel, xmlFile, iChannel++, nChannels);
2924}
2925
2926void WaveTrack::WriteOneXML(const WaveTrack &track, XMLWriter &xmlFile,
2927 size_t iChannel, size_t nChannels)
2928// may throw
2929{
2930 xmlFile.StartTag(wxT("wavetrack"));
2931 track.Track::WriteCommonXMLAttributes(xmlFile);
2932
2933 // Write the "channel" attribute so earlier versions can interpret stereo
2934 // tracks, but this version doesn't read it
2935 {
2936 enum ChannelType {
2937 LeftChannel = 0,
2938 RightChannel = 1,
2939 MonoChannel = 2
2940 };
2941 const auto channelType = (nChannels == 0)
2942 ? MonoChannel
2943 : (iChannel == 0)
2944 ? LeftChannel
2945 : RightChannel;
2946 xmlFile.WriteAttr(wxT("channel"), channelType);
2947 }
2948
2949 xmlFile.WriteAttr(wxT("linked"), static_cast<int>(track.GetLinkType()));
2950 track.WritableSampleTrack::WriteXMLAttributes(xmlFile);
2951 xmlFile.WriteAttr(wxT("rate"), track.GetRate());
2952
2953 // Some values don't vary independently in channels but have been written
2954 // redundantly for each channel. Keep doing this in 3.4 and later in case
2955 // a project is opened in an earlier version.
2956 xmlFile.WriteAttr(wxT("gain"), static_cast<double>(track.GetGain()));
2957 xmlFile.WriteAttr(wxT("pan"), static_cast<double>(track.GetPan()));
2958 xmlFile.WriteAttr(wxT("colorindex"), track.GetWaveColorIndex());
2959
2960 xmlFile.WriteAttr(wxT("sampleformat"), static_cast<long>(track.GetSampleFormat()));
2961
2962 WaveTrackIORegistry::Get().CallWriters(track, xmlFile);
2963
2964 for (const auto &clip : track.mClips)
2965 clip->WriteXML(xmlFile);
2966
2967 xmlFile.EndTag(wxT("wavetrack"));
2968}
2969
2970std::optional<TranslatableString> WaveTrack::GetErrorOpening() const
2971{
2972 assert(IsLeader());
2973 for (const auto pChannel : TrackList::Channels(this))
2974 for (const auto &clip : pChannel->mClips)
2975 for (size_t ii = 0, width = clip->GetWidth(); ii < width; ++ii)
2976 if (clip->GetSequence(ii)->GetErrorOpening())
2977 return XO("A track has a corrupted sample sequence.");
2978
2979 return {};
2980}
2981
2983{
2984 assert(IsLeader());
2985 for (const auto pChannel : TrackList::Channels(this))
2986 for (const auto &clip : pChannel->mClips)
2987 clip->CloseLock();
2988
2989 return true;
2990}
2991
2993 if (mClips.empty())
2994 return nullptr;
2995 return std::min_element(
2996 mClips.begin(), mClips.end(),
2997 [](const auto& a, const auto b) {
2998 return a->GetPlayStartTime() < b->GetPlayStartTime();
2999 })
3000 ->get();
3001}
3002
3004 if (mClips.empty())
3005 return nullptr;
3006 return std::max_element(
3007 mClips.begin(), mClips.end(),
3008 [](const auto& a, const auto b) {
3009 return a->GetPlayEndTime() < b->GetPlayEndTime();
3010 })
3011 ->get();
3012}
3013
3015{
3016 // We're constructing possibly wide clips here, and for this we need to have
3017 // access to the other channel-tracks.
3018 assert(IsLeader());
3019 const auto pOwner = GetOwner();
3020 ClipConstHolders wideClips;
3021 wideClips.reserve(mClips.size());
3022 for (auto clipIndex = 0u; clipIndex < mClips.size(); ++clipIndex)
3023 {
3024 const auto leftClip = mClips[clipIndex];
3025 WaveClipHolder rightClip;
3026 if (NChannels() == 2u && pOwner)
3027 {
3028 const auto& rightClips =
3029 (*TrackList::Channels(this).rbegin())->mClips;
3030 // This is known to have potential for failure for stereo tracks with
3031 // misaligned left/right clips - see
3032 // https://github.com/audacity/audacity/issues/4791.
3033 // If what you are trying to do is something else and this fails,
3034 // please report.
3035 assert(clipIndex < rightClips.size());
3036 if (clipIndex < rightClips.size())
3037 rightClip = rightClips[clipIndex];
3038 }
3039 wideClips.emplace_back(
3040 std::make_shared<WideClip>(leftClip, std::move(rightClip)));
3041 }
3042 return wideClips;
3043}
3044
3046{
3048}
3049
3051{
3052 return ChannelGroup::GetEndTime();
3053}
3054
3055//
3056// Getting/setting samples. The sample counts here are
3057// expressed relative to t=0.0 at the track's sample rate.
3058//
3059
3060std::pair<float, float> WaveChannel::GetMinMax(
3061 double t0, double t1, bool mayThrow) const
3062{
3063 std::pair<float, float> results {
3064 // we need these at extremes to make sure we find true min and max
3065 FLT_MAX, -FLT_MAX
3066 };
3067 bool clipFound = false;
3068
3069 if (t0 > t1) {
3070 if (mayThrow)
3072 return results;
3073 }
3074
3075 if (t0 == t1)
3076 return results;
3077
3078 for (const auto &clip: GetTrack().mClips)
3079 {
3080 if (t1 >= clip->GetPlayStartTime() && t0 <= clip->GetPlayEndTime())
3081 {
3082 clipFound = true;
3083 // TODO wide wave tracks -- choose correct channel
3084 auto clipResults = clip->GetMinMax(0, t0, t1, mayThrow);
3085 if (clipResults.first < results.first)
3086 results.first = clipResults.first;
3087 if (clipResults.second > results.second)
3088 results.second = clipResults.second;
3089 }
3090 }
3091
3092 if(!clipFound)
3093 {
3094 results = { 0.f, 0.f }; // sensible defaults if no clips found
3095 }
3096
3097 return results;
3098}
3099
3100float WaveChannel::GetRMS(double t0, double t1, bool mayThrow) const
3101{
3102 if (t0 > t1) {
3103 if (mayThrow)
3105 return 0.f;
3106 }
3107
3108 if (t0 == t1)
3109 return 0.f;
3110
3111 double sumsq = 0.0;
3112 double duration = 0;
3113
3114 for (const auto &clip: GetTrack().mClips)
3115 {
3116 // If t1 == clip->GetStartTime() or t0 == clip->GetEndTime(), then the clip
3117 // is not inside the selection, so we don't want it.
3118 // if (t1 >= clip->GetStartTime() && t0 <= clip->GetEndTime())
3119 if (t1 >= clip->GetPlayStartTime() && t0 <= clip->GetPlayEndTime())
3120 {
3121 const auto clipStart = std::max(t0, clip->GetPlayStartTime());
3122 const auto clipEnd = std::min(t1, clip->GetPlayEndTime());
3123
3124 // TODO wide wave tracks -- choose correct channel
3125 float cliprms = clip->GetRMS(0, t0, t1, mayThrow);
3126
3127 sumsq += cliprms * cliprms * (clipEnd - clipStart);
3128 duration += (clipEnd - clipStart);
3129 }
3130 }
3131 return duration > 0 ? sqrt(sumsq / duration) : 0.0;
3132}
3133
3134bool WaveTrack::DoGet(size_t iChannel, size_t nBuffers,
3135 const samplePtr buffers[], sampleFormat format,
3136 sampleCount start, size_t len, bool backwards, fillFormat fill,
3137 bool mayThrow, sampleCount* pNumWithinClips) const
3138{
3139 const auto nChannels = NChannels();
3140 assert(iChannel + nBuffers <= nChannels); // precondition
3141 const auto pOwner = GetOwner();
3142 if (!pOwner) {
3144 assert(nChannels == 1);
3145 nBuffers = std::min<size_t>(nBuffers, 1);
3146 }
3147 std::optional<TrackIter<const WaveTrack>> iter;
3148 auto pTrack = this;
3149 if (pOwner) {
3150 const auto ppLeader = TrackList::Channels(this).first;
3151 iter.emplace(ppLeader.advance(IsLeader() ? iChannel : 1));
3152 pTrack = **iter;
3153 }
3154 return std::all_of(buffers, buffers + nBuffers, [&](samplePtr buffer) {
3155 const auto result = pTrack->GetOne(
3156 buffer, format, start, len, backwards, fill, mayThrow,
3157 pNumWithinClips);
3158 if (iter)
3159 pTrack = *(++ *iter);
3160 return result;
3161 });
3162}
3163
3164namespace {
3165void RoundToNearestClipSample(const WaveTrack& track, double& t)
3166{
3167 const auto clip = track.GetClipAtTime(t);
3168 if (!clip)
3169 return;
3170 t = clip->SamplesToTime(clip->TimeToSamples(t - clip->GetPlayStartTime())) +
3171 clip->GetPlayStartTime();
3172}
3173}
3174
3176 double t, size_t iChannel, float* buffer, size_t numSideSamples,
3177 bool mayThrow) const
3178{
3179 const auto numSamplesReadLeft = GetFloatsFromTime(
3180 t, iChannel, buffer, numSideSamples, mayThrow, PlaybackDirection::backward);
3181 const auto numSamplesReadRight = GetFloatsFromTime(
3182 t, iChannel, buffer + numSideSamples, numSideSamples + 1, mayThrow,
3184 return { numSideSamples - numSamplesReadLeft,
3185 numSideSamples + numSamplesReadRight };
3186}
3187
3188namespace
3189{
3190template <typename FloatType>
3191using BufferCharType = std::conditional_t<
3192 std::is_const_v<std::remove_pointer_t<FloatType>>, constSamplePtr,
3193 samplePtr>;
3194
3195template <typename BufferType> struct SampleAccessArgs
3196{
3199 const size_t len;
3200};
3201
3202template <typename BufferType>
3204 const WaveClip& clip, double startOrEndTime /*absolute*/, BufferType buffer,
3205 size_t totalToRead, size_t alreadyRead, bool forward)
3206{
3207 assert(totalToRead >= alreadyRead);
3208 const auto remainingToRead = totalToRead - alreadyRead;
3209 const auto sampsInClip = clip.GetVisibleSampleCount();
3210 const auto sampsPerSec = clip.GetRate() / clip.GetStretchRatio();
3211 if (forward)
3212 {
3213 const auto startTime =
3214 std::max(startOrEndTime - clip.GetPlayStartTime(), 0.);
3215 const sampleCount startSamp { std::round(startTime * sampsPerSec) };
3216 if (startSamp >= sampsInClip)
3217 return { nullptr, sampleCount { 0u }, 0u };
3218 const auto len =
3219 limitSampleBufferSize(remainingToRead, sampsInClip - startSamp);
3220 return { reinterpret_cast<BufferCharType<BufferType>>(
3221 buffer + alreadyRead),
3222 startSamp, len };
3223 }
3224 else
3225 {
3226 const auto endTime = std::min(
3227 startOrEndTime - clip.GetPlayStartTime(), clip.GetPlayDuration());
3228 const sampleCount endSamp { std::round(endTime * sampsPerSec) };
3229 const auto startSamp =
3230 std::max(endSamp - remainingToRead, sampleCount { 0 });
3231 // `len` cannot be greater than `remainingToRead`, itself a `size_t` ->
3232 // safe cast.
3233 const auto len = (endSamp - startSamp).as_size_t();
3234 if (len == 0 || startSamp >= sampsInClip)
3235 return { nullptr, sampleCount { 0u }, 0u };
3236 const auto bufferEnd = buffer + remainingToRead;
3237 return { reinterpret_cast<BufferCharType<BufferType>>(bufferEnd - len),
3238 startSamp, len };
3239 }
3240}
3241} // namespace
3242
3244 double t, size_t iChannel, float* buffer, size_t numSamples, bool mayThrow,
3245 PlaybackDirection direction) const
3246{
3247 RoundToNearestClipSample(*this, t);
3248 auto clip = GetClipAtTime(t);
3249 auto numSamplesRead = 0u;
3250 const auto forward = direction == PlaybackDirection::forward;
3251 while (clip)
3252 {
3253 const auto args = GetSampleAccessArgs(
3254 *clip, t, buffer, numSamples, numSamplesRead, forward);
3255 if (!clip->GetSamples(
3256 iChannel, args.offsetBuffer, floatSample, args.start, args.len,
3257 mayThrow))
3258 return 0u;
3259 numSamplesRead += args.len;
3260 if (numSamplesRead >= numSamples)
3261 break;
3262 clip = GetAdjacentClip(*clip, direction);
3263 }
3264 return numSamplesRead;
3265}
3266
3268 double t, size_t iChannel, float& value, bool mayThrow) const
3269{
3270 const auto clip = GetClipAtTime(t);
3271 if (!clip)
3272 return false;
3273 clip->GetFloatAtTime(
3274 t - clip->GetPlayStartTime(), iChannel, value, mayThrow);
3275 return true;
3276}
3277
3279 double t, size_t iChannel, const float* buffer, size_t numSideSamples,
3280 sampleFormat effectiveFormat)
3281{
3283 t, iChannel, buffer, numSideSamples, effectiveFormat,
3286 t, iChannel, buffer + numSideSamples, numSideSamples + 1, effectiveFormat,
3288}
3289
3291 double t, size_t iChannel, const float* buffer, size_t numSamples,
3292 sampleFormat effectiveFormat, PlaybackDirection direction)
3293{
3294 RoundToNearestClipSample(*this, t);
3295 auto clip = GetClipAtTime(t);
3296 auto numSamplesWritten = 0u;
3297 const auto forward = direction == PlaybackDirection::forward;
3298 while (clip)
3299 {
3300 const auto args = GetSampleAccessArgs(
3301 *clip, t, buffer, numSamples, numSamplesWritten, forward);
3302 if (args.len > 0u)
3303 {
3304 clip->SetSamples(
3305 iChannel, args.offsetBuffer, floatSample, args.start, args.len,
3306 effectiveFormat);
3307 numSamplesWritten += args.len;
3308 if (numSamplesWritten >= numSamples)
3309 break;
3310 }
3311 clip = GetAdjacentClip(*clip, direction);
3312 }
3313}
3314
3316 double t, size_t iChannel, float value, sampleFormat effectiveFormat)
3317{
3318 SetFloatsCenteredAroundTime(t, iChannel, &value, 0u, effectiveFormat);
3319}
3320
3322 double t0, double t1, size_t iChannel,
3323 const std::function<float(double sampleTime)>& producer,
3324 sampleFormat effectiveFormat)
3325{
3326 if (t0 >= t1)
3327 return;
3328 const auto sortedClips = SortedClipArray();
3329 if (sortedClips.empty())
3330 return;
3331 t0 = std::max(t0, (*sortedClips.begin())->GetPlayStartTime());
3332 t1 = std::min(t1, (*sortedClips.rbegin())->GetPlayEndTime());
3333 auto clip = GetClipAtTime(t0);
3334 while (clip) {
3335 const auto clipStartTime = clip->GetPlayStartTime();
3336 const auto clipEndTime = clip->GetPlayEndTime();
3337 const auto sampsPerSec = clip->GetRate() / clip->GetStretchRatio();
3338 const auto roundedT0 =
3339 std::round((t0 - clipStartTime) * sampsPerSec) / sampsPerSec +
3340 clipStartTime;
3341 const auto roundedT1 =
3342 std::round((t1 - clipStartTime) * sampsPerSec) / sampsPerSec +
3343 clipStartTime;
3344 if (clipStartTime > roundedT1)
3345 break;
3346 const auto tt0 = std::max(clipStartTime, roundedT0);
3347 const auto tt1 = std::min(clipEndTime, roundedT1);
3348 const size_t numSamples = (tt1 - tt0) * sampsPerSec + .5;
3349 std::vector<float> values(numSamples);
3350 for (auto i = 0u; i < numSamples; ++i)
3351 values[i] = producer(tt0 + clip->SamplesToTime(i));
3352 clip->SetFloatsFromTime(
3353 tt0 - clipStartTime, iChannel, values.data(), numSamples,
3354 effectiveFormat);
3356 }
3357}
3358
3360 samplePtr buffer, sampleFormat format, sampleCount start, size_t len,
3361 bool backwards, fillFormat fill, bool mayThrow,
3362 sampleCount* pNumWithinClips) const
3363{
3364 if (backwards)
3365 start -= len;
3366 // Simple optimization: When this buffer is completely contained within one clip,
3367 // don't clear anything (because we won't have to). Otherwise, just clear
3368 // everything to be on the safe side.
3369 bool doClear = true;
3370 bool result = true;
3371 sampleCount samplesCopied = 0;
3372 for (const auto &clip: mClips)
3373 {
3374 if (start >= clip->GetPlayStartSample() && start+len <= clip->GetPlayEndSample())
3375 {
3376 doClear = false;
3377 break;
3378 }
3379 }
3380 if (doClear)
3381 {
3382 // Usually we fill in empty space with zero
3383 if (fill == FillFormat::fillZero)
3384 ClearSamples(buffer, format, 0, len);
3385 // but we don't have to.
3386 else if (fill == FillFormat::fillTwo)
3387 {
3388 wxASSERT( format==floatSample );
3389 float * pBuffer = (float*)buffer;
3390 for(size_t i=0;i<len;i++)
3391 pBuffer[i]=2.0f;
3392 }
3393 else
3394 {
3395 wxFAIL_MSG(wxT("Invalid fill format"));
3396 }
3397 }
3398
3399 // Iterate the clips. They are not necessarily sorted by time.
3400 for (const auto &clip: mClips)
3401 {
3402 auto clipStart = clip->GetPlayStartSample();
3403 auto clipEnd = clip->GetPlayEndSample();
3404
3405 if (clipEnd > start && clipStart < start+len)
3406 {
3407 // Yes, exact comparison
3408 if (clip->GetStretchRatio() != 1.0)
3409 return false;
3410
3411 // Clip sample region and Get/Put sample region overlap
3412 auto samplesToCopy =
3413 std::min( start+len - clipStart, clip->GetVisibleSampleCount() );
3414 auto startDelta = clipStart - start;
3415 decltype(startDelta) inclipDelta = 0;
3416 if (startDelta < 0)
3417 {
3418 inclipDelta = -startDelta; // make positive value
3419 samplesToCopy -= inclipDelta;
3420 // samplesToCopy is now either len or
3421 // (clipEnd - clipStart) - (start - clipStart)
3422 // == clipEnd - start > 0
3423 // samplesToCopy is not more than len
3424 //
3425 startDelta = 0;
3426 // startDelta is zero
3427 }
3428 else {
3429 // startDelta is nonnegative and less than len
3430 // samplesToCopy is positive and not more than len
3431 }
3432
3433 if (!clip->GetSamples(0,
3434 (samplePtr)(((char*)buffer) +
3435 startDelta.as_size_t() *
3437 format, inclipDelta, samplesToCopy.as_size_t(), mayThrow ))
3438 result = false;
3439 else
3440 samplesCopied += samplesToCopy;
3441 }
3442 }
3443 if( pNumWithinClips )
3444 *pNumWithinClips = samplesCopied;
3445 if (result == true && backwards)
3446 ReverseSamples(buffer, format, 0, len);
3447 return result;
3448}
3449
3451WaveTrack::GetSampleView(double t0, double t1, bool mayThrow) const
3452{
3453 assert(IsLeader());
3455 for (const auto& channel : Channels()) {
3456 result.push_back(channel->GetSampleView(t0, t1, mayThrow));
3457 }
3458 return result;
3459}
3460
3462WaveChannel::GetSampleView(double t0, double t1, bool mayThrow) const
3463{
3464 std::vector<std::shared_ptr<const WaveChannelInterval>>
3465 intersectingIntervals;
3466 for (const auto& interval : Intervals())
3467 if (interval->Intersects(t0, t1))
3468 intersectingIntervals.push_back(interval);
3469 if (intersectingIntervals.empty())
3470 return { AudioSegmentSampleView {
3471 (TimeToLongSamples(t1) - TimeToLongSamples(t0)).as_size_t() } };
3472 std::sort(
3473 intersectingIntervals.begin(), intersectingIntervals.end(),
3474 [](const auto& a, const auto& b) { return a->Start() < b->Start(); });
3475 std::vector<AudioSegmentSampleView> segments;
3476 segments.reserve(2 * intersectingIntervals.size() + 1);
3477 for (auto i = 0u; i < intersectingIntervals.size();++i)
3478 {
3479 const auto& interval = intersectingIntervals[i];
3480 const auto intervalStartTime = interval->Start();
3481 if (t0 < intervalStartTime)
3482 {
3483 const auto numSamples = TimeToLongSamples(intervalStartTime - t0);
3484 segments.push_back(AudioSegmentSampleView{numSamples.as_size_t()});
3485 t0 = intervalStartTime;
3486 }
3487 const auto intervalT0 = t0 - intervalStartTime;
3488 const auto intervalT1 = std::min(t1, interval->End()) - intervalStartTime;
3489 if(intervalT1 > intervalT0)
3490 {
3491 auto newSegment =
3492 interval->GetSampleView(intervalT0, intervalT1, mayThrow);
3493 t0 += intervalT1 - intervalT0;
3494 segments.push_back(std::move(newSegment));
3495 }
3496 if (t0 == t1)
3497 break;
3498 }
3499 if (t0 < t1)
3500 segments.push_back(AudioSegmentSampleView {
3501 (TimeToLongSamples(t1) - TimeToLongSamples(t0)).as_size_t() });
3502 return segments;
3503}
3504
3507 sampleCount start, size_t len, sampleFormat effectiveFormat)
3508{
3509 for (const auto &clip: GetTrack().mClips)
3510 {
3511 auto clipStart = clip->GetPlayStartSample();
3512 auto clipEnd = clip->GetPlayEndSample();
3513
3514 if (clipEnd > start && clipStart < start+len)
3515 {
3516 // Test as also in WaveTrack::GetOne()
3517 if (clip->GetStretchRatio() != 1.0)
3518 return false;
3519
3520 // Clip sample region and Get/Put sample region overlap
3521 auto samplesToCopy =
3522 std::min( start+len - clipStart, clip->GetVisibleSampleCount() );
3523 auto startDelta = clipStart - start;
3524 decltype(startDelta) inclipDelta = 0;
3525 if (startDelta < 0)
3526 {
3527 inclipDelta = -startDelta; // make positive value
3528 samplesToCopy -= inclipDelta;
3529 // samplesToCopy is now either len or
3530 // (clipEnd - clipStart) - (start - clipStart)
3531 // == clipEnd - start > 0
3532 // samplesToCopy is not more than len
3533 //
3534 startDelta = 0;
3535 // startDelta is zero
3536 }
3537 else {
3538 // startDelta is nonnegative and less than len
3539 // samplesToCopy is positive and not more than len
3540 }
3541
3542 clip->SetSamples(0,
3543 buffer + startDelta.as_size_t() * SAMPLE_SIZE(format),
3544 format, inclipDelta, samplesToCopy.as_size_t(), effectiveFormat );
3545 clip->MarkChanged();
3546 }
3547 }
3548 return true;
3549}
3550
3552{
3553 auto result = narrowestSampleFormat;
3554 const auto accumulate = [&](const WaveTrack &track) {
3555 for (const auto &pClip : track.GetClips())
3556 for (size_t ii = 0, width = pClip->GetWidth(); ii < width; ++ii)
3557 result = std::max(result,
3558 pClip->GetSequence(ii)->GetSampleFormats().Effective());
3559 };
3560 if (auto pOwner = GetOwner()) {
3561 for (auto channel : TrackList::Channels(this))
3562 accumulate(*channel);
3563 }
3564 else
3565 accumulate(*this);
3566 return result;
3567}
3568
3570{
3571 auto pTrack = this;
3572 if (GetOwner())
3573 // Substitute the leader track
3574 pTrack = *TrackList::Channels(this).begin();
3575 auto &clips = pTrack->GetClips();
3576 return std::all_of(clips.begin(), clips.end(),
3577 [](const auto &pClip){ return pClip->GetEnvelope()->IsTrivial(); });
3578}
3579
3581 double* buffer, size_t bufferLen, double t0, bool backwards) const
3582{
3583 auto pTrack = this;
3584 if (GetOwner())
3585 // Substitute the leader track
3586 pTrack = *TrackList::Channels(this).begin();
3587
3588 if (backwards)
3589 t0 -= bufferLen / GetRate();
3590 // The output buffer corresponds to an unbroken span of time which the callers expect
3591 // to be fully valid. As clips are processed below, the output buffer is updated with
3592 // envelope values from any portion of a clip, start, end, middle, or none at all.
3593 // Since this does not guarantee that the entire buffer is filled with values we need
3594 // to initialize the entire buffer to a default value.
3595 //
3596 // This does mean that, in the cases where a usable clip is located, the buffer value will
3597 // be set twice. Unfortunately, there is no easy way around this since the clips are not
3598 // stored in increasing time order. If they were, we could just track the time as the
3599 // buffer is filled.
3600 for (decltype(bufferLen) i = 0; i < bufferLen; i++)
3601 {
3602 buffer[i] = 1.0;
3603 }
3604
3605 double startTime = t0;
3606 const auto rate = GetRate();
3607 auto tstep = 1.0 / rate;
3608 double endTime = t0 + tstep * bufferLen;
3609 for (const auto &clip: pTrack->mClips)
3610 {
3611 // IF clip intersects startTime..endTime THEN...
3612 auto dClipStartTime = clip->GetPlayStartTime();
3613 auto dClipEndTime = clip->GetPlayEndTime();
3614 if ((dClipStartTime < endTime) && (dClipEndTime > startTime))
3615 {
3616 auto rbuf = buffer;
3617 auto rlen = bufferLen;
3618 auto rt0 = t0;
3619
3620 if (rt0 < dClipStartTime)
3621 {
3622 // This is not more than the number of samples in
3623 // (endTime - startTime) which is bufferLen:
3624 auto nDiff = (sampleCount)floor((dClipStartTime - rt0) * rate + 0.5);
3625 auto snDiff = nDiff.as_size_t();
3626 rbuf += snDiff;
3627 wxASSERT(snDiff <= rlen);
3628 rlen -= snDiff;
3629 rt0 = dClipStartTime;
3630 }
3631
3632 if (rt0 + rlen*tstep > dClipEndTime)
3633 {
3634 auto nClipLen = clip->GetPlayEndSample() - clip->GetPlayStartSample();
3635
3636 if (nClipLen <= 0) // Testing for bug 641, this problem is consistently '== 0', but doesn't hurt to check <.
3637 return;
3638
3639 // This check prevents problem cited in http://bugzilla.audacityteam.org/show_bug.cgi?id=528#c11,
3640 // Gale's cross_fade_out project, which was already corrupted by bug 528.
3641 // This conditional prevents the previous write past the buffer end, in clip->GetEnvelope() call.
3642 // Never increase rlen here.
3643 // PRL bug 827: rewrote it again
3644 rlen = limitSampleBufferSize( rlen, nClipLen );
3645 rlen = std::min(rlen, size_t(floor(0.5 + (dClipEndTime - rt0) / tstep)));
3646 }
3647 // Samples are obtained for the purpose of rendering a wave track,
3648 // so quantize time
3649 clip->GetEnvelope()->GetValues(rbuf, rlen, rt0, tstep);
3650 }
3651 }
3652 if (backwards)
3653 std::reverse(buffer, buffer + bufferLen);
3654}
3655
3657 const WaveClip& clip, PlaybackDirection direction) const
3658{
3659 const auto neighbour = GetNextClip(clip, direction);
3660 if (!neighbour)
3661 return nullptr;
3662 else if (direction == PlaybackDirection::forward)
3663 return std::abs(clip.GetPlayEndTime() - neighbour->GetPlayStartTime()) <
3664 1e-9 ?
3665 neighbour :
3666 nullptr;
3667 else
3668 return std::abs(clip.GetPlayStartTime() - neighbour->GetPlayEndTime()) <
3669 1e-9 ?
3670 neighbour :
3671 nullptr;
3672}
3673
3674WaveClip*
3676{
3677 return const_cast<WaveClip*>(
3678 std::as_const(*this).GetAdjacentClip(clip, direction));
3679}
3680
3681// When the time is both the end of a clip and the start of the next clip, the
3682// latter clip is returned.
3684{
3685 return const_cast<WaveClip*>(std::as_const(*this).GetClipAtTime(time));
3686}
3687
3689 const WaveClip& clip, PlaybackDirection direction) const
3690{
3691 const auto clips = SortedClipArray();
3692 const auto p = std::find(clips.begin(), clips.end(), &clip);
3693 if (p == clips.end())
3694 return nullptr;
3695 else if (direction == PlaybackDirection::forward)
3696 return p == clips.end() - 1 ? nullptr : *(p + 1);
3697 else
3698 return p == clips.begin() ? nullptr : *(p - 1);
3699}
3700
3702{
3703 assert(t0 <= t1);
3704 WaveClipConstHolders intersectingClips;
3705 for (const auto& clip : mClips)
3706 if (clip->IntersectsPlayRegion(t0, t1))
3707 intersectingClips.push_back(clip);
3708 return intersectingClips;
3709}
3710
3711WaveClip*
3713{
3714 return const_cast<WaveClip*>(
3715 std::as_const(*this).GetNextClip(clip, direction));
3716}
3717
3718const WaveClip* WaveTrack::GetClipAtTime(double time) const
3719{
3720 const auto clips = SortedClipArray();
3721 auto p = std::find_if(
3722 clips.rbegin(), clips.rend(), [&](const WaveClip* const& clip) {
3723 return clip->WithinPlayRegion(time);
3724 });
3725
3726 // When two clips are immediately next to each other, the GetPlayEndTime() of the first clip
3727 // and the GetPlayStartTime() of the second clip may not be exactly equal due to rounding errors.
3728 // If "time" is the end time of the first of two such clips, and the end time is slightly
3729 // less than the start time of the second clip, then the first rather than the
3730 // second clip is found by the above code. So correct this.
3731 if (p != clips.rend() && p != clips.rbegin() &&
3732 time == (*p)->GetPlayEndTime() &&
3733 (*p)->SharesBoundaryWithNextClip(*(p-1))) {
3734 p--;
3735 }
3736
3737 return p != clips.rend() ? *p : nullptr;
3738}
3739
3741{
3742 auto pTrack = this;
3743 if (GetOwner())
3744 // Substitute the leader track
3745 pTrack = *TrackList::Channels(this).begin();
3746 WaveClip* clip = pTrack->GetClipAtTime(time);
3747 if (clip)
3748 return clip->GetEnvelope();
3749 else
3750 return NULL;
3751}
3752
3753void WaveTrack::CreateWideClip(double offset, const wxString& name)
3754{
3755 assert(IsLeader());
3756 for(auto channel : TrackList::Channels(this))
3757 channel->CreateClip(offset, name);
3758}
3759
3760WaveClip* WaveTrack::CreateClip(double offset, const wxString& name)
3761{
3762 // TODO wide wave tracks -- choose clip width correctly for the track
3763 auto clip = std::make_shared<WaveClip>(1,
3765 clip->SetName(name);
3766 clip->SetSequenceStartTime(offset);
3767
3768 const auto& tempo = GetProjectTempo();
3769 if (tempo.has_value())
3770 clip->OnProjectTempoChange(std::nullopt, *tempo);
3771 mClips.push_back(std::move(clip));
3772
3773 auto result = mClips.back().get();
3774 // TODO wide wave tracks -- for now assertion is correct because widths are
3775 // always 1
3776 assert(result->GetWidth() == GetWidth());
3777 return result;
3778}
3779
3781{
3782 if (mClips.empty()) {
3783 return CreateClip(WaveTrackData::Get(*this).GetOrigin(), MakeNewClipName());
3784 }
3785 else
3786 return mClips.back().get();
3787}
3788
3791{
3792 if (mClips.empty()) {
3793 return CreateClip(WaveTrackData::Get(*this).GetOrigin(), MakeNewClipName());
3794 }
3795 else
3796 {
3797 auto it = mClips.begin();
3798 WaveClip *rightmost = (*it++).get();
3799 double maxOffset = rightmost->GetPlayStartTime();
3800 for (auto end = mClips.end(); it != end; ++it)
3801 {
3802 WaveClip *clip = it->get();
3803 double offset = clip->GetPlayStartTime();
3804 if (maxOffset < offset)
3805 maxOffset = offset, rightmost = clip;
3806 }
3807 return rightmost;
3808 }
3809}
3810
3811int WaveTrack::GetClipIndex(const WaveClip* clip) const
3812{
3813 int result;
3814 FindClip(mClips, clip, &result);
3815 return result;
3816}
3817
3819{
3820 if(index < (int)mClips.size())
3821 return mClips[index].get();
3822 else
3823 return nullptr;
3824}
3825
3827{
3828 return const_cast<WaveTrack&>(*this).GetClipByIndex(index);
3829}
3830
3832{
3833 return mClips.size();
3834}
3835
3836int WaveTrack::GetNumClips(double t0, double t1) const
3837{
3838 const auto clips = SortedClipArray();
3839 // Find first position where the comparison is false
3840 const auto firstIn = std::lower_bound(clips.begin(), clips.end(), t0,
3841 [](const auto& clip, double t0) {
3842 return clip->GetPlayEndTime() <= t0;
3843 });
3844 // Find first position where the comparison is false
3845 const auto firstOut = std::lower_bound(firstIn, clips.end(), t1,
3846 [](const auto& clip, double t1) {
3847 return clip->GetPlayStartTime() < t1;
3848 });
3849 return std::distance(firstIn, firstOut);
3850}
3851
3853 const std::vector<WaveClip*> &clips,
3854 double amount,
3855 double *allowedAmount /* = NULL */)
3856{
3857 if (allowedAmount)
3858 *allowedAmount = amount;
3859
3860 const auto &moving = [&](WaveClip *clip){
3861 // linear search might be improved, but expecting few moving clips
3862 // compared with the fixed clips
3863 return clips.end() != std::find( clips.begin(), clips.end(), clip );
3864 };
3865
3866 for (const auto &c: mClips) {
3867 if ( moving( c.get() ) )
3868 continue;
3869 for (const auto clip : clips) {
3870 if (c->GetPlayStartTime() < clip->GetPlayEndTime() + amount &&
3871 c->GetPlayEndTime() > clip->GetPlayStartTime() + amount)
3872 {
3873 if (!allowedAmount)
3874 return false; // clips overlap
3875
3876 if (amount > 0)
3877 {
3878 if (c->GetPlayStartTime() - clip->GetPlayEndTime() < *allowedAmount)
3879 *allowedAmount = c->GetPlayStartTime() - clip->GetPlayEndTime();
3880 if (*allowedAmount < 0)
3881 *allowedAmount = 0;
3882 } else
3883 {
3884 if (c->GetPlayEndTime() - clip->GetPlayStartTime() > *allowedAmount)
3885 *allowedAmount = c->GetPlayEndTime() - clip->GetPlayStartTime();
3886 if (*allowedAmount > 0)
3887 *allowedAmount = 0;
3888 }
3889 }
3890 }
3891 }
3892
3893 if (allowedAmount)
3894 {
3895 if (*allowedAmount == amount)
3896 return true;
3897
3898 // Check if the NEW calculated amount would not violate
3899 // any other constraint
3900 if (!CanOffsetClips(clips, *allowedAmount, nullptr)) {
3901 *allowedAmount = 0; // play safe and don't allow anything
3902 return false;
3903 }
3904 else
3905 return true;
3906 } else
3907 return true;
3908}
3909
3911 const WaveClip& candidateClip, double& slideBy, double tolerance) const
3912{
3913 if (mClips.empty())
3914 return true;
3915 // Find clip in this that overlaps most with `clip`:
3916 const auto candidateClipStartTime = candidateClip.GetPlayStartTime();
3917 const auto candidateClipEndTime = candidateClip.GetPlayEndTime();
3918 const auto t0 = SnapToSample(candidateClipStartTime + slideBy);
3919 const auto t1 = SnapToSample(candidateClipEndTime + slideBy);
3920 std::vector<double> overlaps;
3921 std::transform(
3922 mClips.begin(), mClips.end(), std::back_inserter(overlaps),
3923 [&](const auto& pClip) {
3924 return pClip->IntersectsPlayRegion(t0, t1) ?
3925 std::min(pClip->GetPlayEndTime(), t1) -
3926 std::max(pClip->GetPlayStartTime(), t0) :
3927 0.0;
3928 });
3929 const auto maxOverlap = std::max_element(overlaps.begin(), overlaps.end());
3930 if (*maxOverlap > tolerance)
3931 return false;
3932 const auto overlappedClip =
3933 mClips[std::distance(overlaps.begin(), maxOverlap)];
3934 const auto requiredOffset = slideBy +
3935 *maxOverlap * (overlappedClip->GetPlayStartTime() < t0 ? 1 : -1);
3936 // Brute-force check to see if there's another clip that'd be in the way.
3937 if (std::any_of(
3938 mClips.begin(), mClips.end(),
3939 [&](const auto& pClip)
3940 {
3941 const auto result = pClip->IntersectsPlayRegion(
3942 SnapToSample(candidateClipStartTime + requiredOffset),
3943 SnapToSample(candidateClipEndTime + requiredOffset));
3944 return result;
3945 }))
3946 return false;
3947 slideBy = requiredOffset;
3948 return true;
3949}
3950
3952void WaveTrack::Split(double t0, double t1)
3953{
3954 assert(IsLeader());
3955 for (const auto pChannel : TrackList::Channels(this)) {
3956 pChannel->SplitAt(t0);
3957 if (t0 != t1)
3958 pChannel->SplitAt(t1);
3959 }
3960}
3961
3964{
3965 for (const auto &c : mClips)
3966 {
3967 if (c->SplitsPlayRegion(t))
3968 {
3969 t = SnapToSample(t);
3970 auto newClip = std::make_shared<WaveClip>(*c, mpFactory, true);
3971 c->TrimRightTo(t);// put t on a sample
3972 newClip->TrimLeftTo(t);
3973
3974 // This could invalidate the iterators for the loop! But we return
3975 // at once so it's okay
3976 InsertClip(std::move(newClip)); // transfer ownership
3977 return;
3978 }
3979 }
3980}
3981
3982// Expand cut line (that is, re-insert audio, then DELETE audio saved in cut line)
3983// Can't promise strong exception safety for a pair of tracks together
3984void WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
3985 double* cutlineEnd)
3986{
3987 assert(IsLeader());
3988 for (const auto pChannel : TrackList::Channels(this)) {
3989 pChannel->ExpandOneCutLine(cutLinePosition, cutlineStart, cutlineEnd);
3990 // Assign the out parameters at most once
3991 cutlineStart = cutlineEnd = nullptr;
3992 }
3993}
3994
3996void WaveTrack::ExpandOneCutLine(double cutLinePosition,
3997 double* cutlineStart, double* cutlineEnd)
3998{
3999 bool editClipCanMove = GetEditClipsCanMove();
4000
4001 // Find clip which contains this cut line
4002 double start = 0, end = 0;
4003 auto pEnd = mClips.end();
4004 auto pClip = std::find_if( mClips.begin(), pEnd,
4005 [&](const WaveClipHolder &clip) {
4006 return clip->FindCutLine(cutLinePosition, &start, &end); } );
4007 if (pClip != pEnd)
4008 {
4009 auto &clip = *pClip;
4010 if (!editClipCanMove)
4011 {
4012 // We are not allowed to move the other clips, so see if there
4013 // is enough room to expand the cut line
4014 for (const auto &clip2: mClips)
4015 {
4016 if (clip2->GetPlayStartTime() > clip->GetPlayStartTime() &&
4017 clip->GetPlayEndTime() + end - start > clip2->GetPlayStartTime())
4018 // Strong-guarantee in case of this path
4021 XO("There is not enough room available to expand the cut line"),
4022 XO("Warning"),
4023 "Error:_Insufficient_space_in_track"
4024 };
4025 }
4026 }
4027
4028 clip->ExpandCutLine(cutLinePosition);
4029
4030 // Strong-guarantee provided that the following gives No-fail-guarantee
4031
4032 if (cutlineStart)
4033 *cutlineStart = start;
4034 if (cutlineEnd)
4035 *cutlineEnd = end;
4036
4037 // Move clips which are to the right of the cut line
4038 if (editClipCanMove)
4039 {
4040 for (const auto &clip2 : mClips)
4041 {
4042 if (clip2->GetPlayStartTime() > clip->GetPlayStartTime())
4043 clip2->ShiftBy(end - start);
4044 }
4045 }
4046 }
4047}
4048
4049bool WaveTrack::RemoveCutLine(double cutLinePosition)
4050{
4051 assert(IsLeader());
4052
4053 bool removed = false;
4054 for (const auto pChannel : TrackList::Channels(this))
4055 for (const auto &clip : pChannel->mClips)
4056 if (clip->RemoveCutLine(cutLinePosition)) {
4057 removed = true;
4058 break;
4059 }
4060
4061 return removed;
4062}
4063
4064// Can't promise strong exception safety for a pair of tracks together
4065bool WaveTrack::MergeClips(int clipidx1, int clipidx2)
4066{
4067 const auto channels = TrackList::Channels(this);
4068 return std::all_of(channels.begin(), channels.end(),
4069 [&](const auto pChannel){
4070 return pChannel->MergeOneClipPair(clipidx1, clipidx2); });
4071}
4072
4074bool WaveTrack::MergeOneClipPair(int clipidx1, int clipidx2)
4075{
4076 WaveClip* clip1 = GetClipByIndex(clipidx1);
4077 WaveClip* clip2 = GetClipByIndex(clipidx2);
4078
4079 if (!clip1 || !clip2) // Could happen if one track of a linked pair had a split and the other didn't.
4080 return false; // Don't throw, just do nothing.
4081
4082 const auto stretchRatiosEqual = clip1->HasEqualStretchRatio(*clip2);
4083 if (!stretchRatiosEqual)
4084 return false;
4085
4086 // Append data from second clip to first clip
4087 // use Strong-guarantee
4088 bool success = clip1->Paste(clip1->GetPlayEndTime(), *clip2);
4089 assert(success); // assuming clips of the same track must have same width
4090
4091 // use No-fail-guarantee for the rest
4092 // Delete second clip
4093 auto it = FindClip(mClips, clip2);
4094 mClips.erase(it);
4095
4096 return true;
4097}
4098
4100 const std::vector<IntervalHolder>& srcIntervals,
4101 const ProgressReporter& reportProgress)
4102{
4103 std::vector<IntervalHolder> dstIntervals;
4104 dstIntervals.reserve(srcIntervals.size());
4105 std::transform(
4106 srcIntervals.begin(), srcIntervals.end(),
4107 std::back_inserter(dstIntervals), [&](const IntervalHolder& interval) {
4108 return interval->GetStretchRenderedCopy(
4109 reportProgress, *this, mpFactory, GetSampleFormat());
4110 });
4111
4112 // If we reach this point it means that no error was thrown - we can replace
4113 // the source with the destination intervals.
4114 for (auto i = 0; i < srcIntervals.size(); ++i)
4115 ReplaceInterval(srcIntervals[i], dstIntervals[i]);
4116}
4117
4119{
4120 assert(IsLeader());
4121 auto channel = 0;
4122 for (const auto pChannel : TrackList::Channels(this))
4123 {
4124 const auto clip = interval->GetClip(channel++);
4125 if (clip)
4126 pChannel->InsertClip(clip);
4127 }
4128}
4129
4131{
4132 assert(IsLeader());
4133 auto channel = 0;
4134 for (const auto pChannel : TrackList::Channels(this))
4135 {
4136 const auto clip = interval->GetClip(channel);
4137 if (clip)
4138 pChannel->RemoveAndReturnClip(clip.get());
4139 ++channel;
4140 }
4141}
4142
4144 const IntervalHolder& oldOne, const IntervalHolder& newOne)
4145{
4146 assert(IsLeader());
4147 assert(oldOne->NChannels() == newOne->NChannels());
4148 RemoveInterval(oldOne);
4149 InsertInterval(newOne);
4150 newOne->SetName(oldOne->GetName());
4151}
4152
4156{
4157 for (const auto pChannel : TrackList::Channels(this)) {
4158 for (const auto &clip : pChannel->mClips)
4159 clip->Resample(rate, progress);
4160 }
4161 DoSetRate(rate);
4162}
4163
4165 const ProgressReport &progress)
4166{
4167 size_t count = 0;
4168 const auto range = TrackList::Channels(this);
4169 const auto myProgress = [&](double fraction){
4170 return progress((count + fraction) / range.size());
4171 };
4172 for (const auto pChannel : range) {
4173 if (!ReverseOne(*pChannel, start, len, myProgress))
4174 return false;
4175 ++count;
4176 }
4177 return true;
4178}
4179
4181 sampleCount start, sampleCount len,
4182 const ProgressReport &progress)
4183{
4184 bool rValue = true; // return value
4185
4186 // start, end, len refer to the selected reverse region
4187 auto end = start + len;
4188
4189 // STEP 1:
4190 // If a reverse selection begins and/or ends at the inside of a clip
4191 // perform a split at the start and/or end of the reverse selection
4192 const auto &clips = track.GetClips();
4193 // Beware, the array grows as we loop over it. Use integer subscripts, not
4194 // iterators.
4195 for (size_t ii = 0; ii < clips.size(); ++ii) {
4196 const auto &clip = clips[ii].get();
4197 auto clipStart = clip->GetPlayStartSample();
4198 auto clipEnd = clip->GetPlayEndSample();
4199 if (clipStart < start && clipEnd > start && clipEnd <= end) {
4200 // the reverse selection begins at the inside of a clip
4201 double splitTime = track.LongSamplesToTime(start);
4202 track.SplitAt(splitTime);
4203 }
4204 else if (clipStart >= start && clipStart < end && clipEnd > end) {
4205 // the reverse selection ends at the inside of a clip
4206 double splitTime = track.LongSamplesToTime(end);
4207 track.SplitAt(splitTime);
4208 }
4209 else if (clipStart < start && clipEnd > end) {
4210 // the selection begins AND ends at the inside of a clip
4211 double splitTime = track.LongSamplesToTime(start);
4212 track.SplitAt(splitTime);
4213 splitTime = track.LongSamplesToTime(end);
4214 track.SplitAt(splitTime);
4215 }
4216 }
4217
4218 //STEP 2:
4219 // Individually reverse each clip inside the selected region
4220 // and apply the appropriate offset after detaching them from the track
4221
4222 bool checkedFirstClip = false;
4223
4224 // used in calculating the offset of clips to rearrange
4225 // holds the new end position of the current clip
4226 auto currentEnd = end;
4227
4228 // holds the reversed clips
4229 WaveClipHolders revClips;
4230 // holds the clips that appear after the reverse selection region
4231 WaveClipHolders otherClips;
4232 auto clipArray = track.SortedClipArray();
4233 for (size_t i = 0; i < clipArray.size(); ++i) {
4234 WaveClip *clip = clipArray[i];
4235 auto clipStart = clip->GetPlayStartSample();
4236 auto clipEnd = clip->GetPlayEndSample();
4237
4238 if (clipStart >= start && clipEnd <= end) {
4239 // if the clip is inside the selected region
4240 // this is used to check if the selected region begins with a
4241 // whitespace. If yes then clipStart (of the first clip) and start are
4242 // not the same. Adjust currentEnd accordingly and set endMerge to
4243 // false
4244 if (!checkedFirstClip && clipStart > start) {
4245 checkedFirstClip = true;
4246 if (i > 0) {
4247 if (clipArray[i - 1]->GetPlayEndSample() <= start)
4248 currentEnd -= (clipStart - start);
4249 }
4250 else
4251 currentEnd -= (clipStart - start);
4252 }
4253
4254 auto revStart = std::max(clipStart, start);
4255 auto revEnd = std::min(end, clipEnd);
4256 auto revLen = revEnd - revStart;
4257 if (revEnd >= revStart) {
4258 // reverse the clip
4259 if (!ReverseOneClip(track, revStart, revLen, start, end, progress))
4260 {
4261 rValue = false;
4262 break;
4263 }
4264
4265 // calculate the offset required
4266 auto clipOffsetStart = currentEnd - (clipEnd - clipStart);
4267 double offsetStartTime = track.LongSamplesToTime(clipOffsetStart);
4268 if (i + 1 < clipArray.size()) {
4269 // update currentEnd if there is a clip to process next
4270 auto nextClipStart = clipArray[i + 1]->GetPlayStartSample();
4271 currentEnd = currentEnd -
4272 (clipEnd - clipStart) - (nextClipStart - clipEnd);
4273 }
4274
4275 // detach the clip from track
4276 revClips.push_back(track.RemoveAndReturnClip(clip));
4277 // align time to a sample and set offset
4278 revClips.back()->SetPlayStartTime(
4279 track.SnapToSample(offsetStartTime));
4280 }
4281 }
4282 else if (clipStart >= end) {
4283 // clip is after the selection region
4284 // simply remove and append to otherClips
4285 otherClips.push_back(track.RemoveAndReturnClip(clip));
4286 }
4287 }
4288
4289 // STEP 3: Append the clips from
4290 // revClips and otherClips back to the track
4291 // the last clip of revClips is appended to the track first
4292 // PRL: I don't think that matters, the sequence of storage of clips in the
4293 // track is not elsewhere assumed to be by time
4294 for (auto it = revClips.rbegin(), revEnd = revClips.rend();
4295 rValue && it != revEnd; ++it)
4296 rValue = track.AddClip(*it);
4297
4298 if (!rValue)
4299 return false;
4300
4301 for (auto &clip : otherClips)
4302 if (!(rValue = track.AddClip(clip)))
4303 break;
4304
4305 return rValue;
4306}
4307
4309 sampleCount start, sampleCount len,
4310 sampleCount originalStart, sampleCount originalEnd,
4311 const ProgressReport &report)
4312{
4313 bool rc = true;
4314 // keep track of two blocks whose data we will swap
4315 auto first = start;
4316
4317 auto blockSize = track.GetMaxBlockSize();
4318 Floats buffer1{ blockSize };
4319 const auto pBuffer1 = buffer1.get();
4320 Floats buffer2{ blockSize };
4321 const auto pBuffer2 = buffer2.get();
4322
4323 auto originalLen = originalEnd - originalStart;
4324
4325 while (len > 1) {
4326 auto block =
4327 limitSampleBufferSize(track.GetBestBlockSize(first), len / 2);
4328 auto second = first + (len - block);
4329
4330 track.GetFloats(buffer1.get(), first, block);
4331 std::reverse(pBuffer1, pBuffer1 + block);
4332 track.GetFloats(buffer2.get(), second, block);
4333 std::reverse(pBuffer2, pBuffer2 + block);
4334 // Don't dither on later rendering if only reversing samples
4335 const bool success =
4336 track.Set((samplePtr)buffer2.get(), floatSample, first, block,
4338 &&
4339 track.Set((samplePtr)buffer1.get(), floatSample, second, block,
4341 if (!success)
4342 return false;
4343
4344 len -= 2 * block;
4345 first += block;
4346
4347 if (!report(
4348 2 * (first - originalStart).as_double() / originalLen.as_double()
4349 )) {
4350 rc = false;
4351 break;
4352 }
4353 }
4354
4355 return rc;
4356}
4357
4358namespace {
4359 template < typename Cont1, typename Cont2 >
4360 Cont1 FillSortedClipArray(const Cont2& mClips)
4361 {
4362 Cont1 clips;
4363 for (const auto &clip : mClips)
4364 clips.push_back(clip.get());
4365 std::sort(clips.begin(), clips.end(),
4366 [](const WaveClip *a, const WaveClip *b)
4367 { return a->GetPlayStartTime() < b->GetPlayStartTime(); });
4368 return clips;
4369 }
4370}
4371
4373{
4374 return FillSortedClipArray<WaveClipPointers>(mClips);
4375}
4376
4378{
4379 return FillSortedClipArray<WaveClipConstPointers>(mClips);
4380}
4381
4383{
4384 assert(IsLeader());
4385 for (const auto pChannel : TrackList::Channels(this))
4386 for (const auto& clip : pChannel->GetClips())
4387 if (clip->GetTrimLeft() != 0 || clip->GetTrimRight() != 0)
4388 return true;
4389 return false;
4390}
4391
4393{
4394 assert(IsLeader());
4395 for (const auto pChannel : TrackList::Channels(this)) {
4396 for (auto clip : pChannel->GetClips()) {
4397 if (clip->GetTrimLeft() != 0) {
4398 auto t0 = clip->GetPlayStartTime();
4399 clip->SetTrimLeft(0);
4400 clip->ClearLeft(t0);
4401 }
4402 if (clip->GetTrimRight() != 0) {
4403 auto t1 = clip->GetPlayEndTime();
4404 clip->SetTrimRight(0);
4405 clip->ClearRight(t1);
4406 }
4407 }
4408 }
4409}
4410
4412{
4413 // The unspecified sequence is a post-order, but there is no
4414 // promise whether sister nodes are ordered in time.
4415 if ( !mStack.empty() ) {
4416 auto &pair = mStack.back();
4417 if ( ++pair.first == pair.second ) {
4418 mStack.pop_back();
4419 }
4420 else
4421 push( (*pair.first)->GetCutLines() );
4422 }
4423
4424 return *this;
4425}
4426
4428{
4429 auto pClips = &clips;
4430 while (!pClips->empty()) {
4431 auto first = pClips->begin();
4432 mStack.push_back( Pair( first, pClips->end() ) );
4433 pClips = &(*first)->GetCutLines();
4434 }
4435}
4436
4438 SampleBlockIDSet *pIDs)
4439{
4440 for (auto wt : tracks.Any<const WaveTrack>())
4441 for (const auto pChannel : TrackList::Channels(wt))
4442 // Scan all clips within current track
4443 for (const auto &clip : pChannel->GetAllClips())
4444 // Scan all sample blocks within current clip
4445 for (size_t ii = 0, width = clip->GetWidth(); ii < width; ++ii) {
4446 auto blocks = clip->GetSequenceBlockArray(ii);
4447 for (const auto &block : *blocks) {
4448 auto &pBlock = block.sb;
4449 if (pBlock) {
4450 if (pIDs && !pIDs->insert(pBlock->GetBlockID()).second)
4451 continue;
4452 if (visitor)
4453 visitor(*pBlock);
4454 }
4455 }
4456 }
4457}
4458
4460 SampleBlockIDSet *pIDs)
4461{
4463 const_cast<TrackList &>(tracks), std::move( inspector ), pIDs );
4464}
4465
4467 return std::make_shared< WaveTrackFactory >(
4470};
4471
4474};
4475
4477{
4478 return project.AttachedObjects::Get< WaveTrackFactory >( key2 );
4479}
4480
4482{
4483 return Get( const_cast< AudacityProject & >( project ) );
4484}
4485
4487{
4488 auto result = TrackFactoryFactory( project );
4489 project.AttachedObjects::Assign( key2, result );
4490 return *result;
4491}
4492
4494{
4495 project.AttachedObjects::Assign( key2, nullptr );
4496}
4497
4498namespace {
4499// If any clips have hidden data, don't allow older versions to open the
4500// project. Otherwise overlapping clips might result.
4503 const TrackList& trackList = TrackList::Get(project);
4504 for (auto wt : trackList.Any<const WaveTrack>())
4505 for (const auto pChannel : TrackList::Channels(wt))
4506 for (const auto& clip : pChannel->GetAllClips())
4507 if (clip->GetTrimLeft() > 0.0 || clip->GetTrimRight() > 0.0)
4508 return { 3, 1, 0, 0 };
4510 }
4511);
4512
4513// If any clips have any stretch, don't allow older versions to open the
4514// project. Otherwise overlapping clips might result.
4517 const TrackList& trackList = TrackList::Get(project);
4518 for (auto wt : trackList.Any<const WaveTrack>())
4519 for (const auto pChannel : TrackList::Channels(wt))
4520 for (const auto& clip : pChannel->GetAllClips())
4521 if (clip->GetStretchRatio() != 1.0)
4522 return { 3, 4, 0, 0 };
4524 }
4525);
4526}
4527
4529 L"/GUI/TrackNames/DefaultTrackName",
4530 // Computed default value depends on chosen language
4531 []{ return DefaultName.Translation(); }
4532};
4533
4534// Bug 825 is essentially that SyncLock requires EditClipsCanMove.
4535// SyncLock needs rethinking, but meanwhile this function
4536// fixes the issues of Bug 825 by allowing clips to move when in
4537// SyncLock.
4539{
4540 bool mIsSyncLocked = SyncLockTracks.Read();
4541 if( mIsSyncLocked )
4542 return true;
4543 bool editClipsCanMove;
4544 return EditClipsCanMove.Read();
4545}
4546
4548 L"/GUI/EditClipCanMove", false };
4549
wxT("CloseDown"))
@ 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.
int min(int a, int b)
EffectDistortionSettings params
Definition: Distortion.cpp:77
const TranslatableString name
Definition: Distortion.cpp:76
const wxChar * values
XO("Cut/Copy/Paste")
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
#define XC(s, c)
Definition: Internat.h:37
std::vector< std::vector< AudioSegmentSampleView > > ChannelGroupSampleView
Definition: MixerBoard.h:35
PlaybackDirection
const ProjectFormatVersion BaseProjectFormatVersion
This is a helper constant for the "most compatible" project version with the value (3,...
an object holding per-project preferred sample rate
std::shared_ptr< SampleBlockFactory > SampleBlockFactoryPtr
Definition: SampleBlock.h:30
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
void ReverseSamples(samplePtr dst, sampleFormat format, int start, int len)
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
char * samplePtr
Definition: SampleFormat.h:57
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52
const char * constSamplePtr
Definition: SampleFormat.h:58
enum FillFormat fillFormat
BoolSetting SyncLockTracks
Definition: SyncLock.cpp:173
const auto tracks
const auto project
Contains declarations for TimeWarper, IdentityTimeWarper, ShiftTimeWarper, LinearTimeWarper,...
std::function< void(double)> ProgressReporter
Definition: Track.h:53
std::shared_ptr< TrackList > TrackListHolder
Definition: Track.h:42
WaveTrack::Region Region
std::shared_ptr< WaveClip > WaveClipHolder
Definition: WaveClip.h:45
std::vector< WaveClipHolder > WaveClipHolders
Definition: WaveClip.h:46
std::vector< std::shared_ptr< const WaveClip > > WaveClipConstHolders
Definition: WaveClip.h:47
bool GetEditClipsCanMove()
Definition: WaveTrack.cpp:4538
static ProjectFileIORegistry::ObjectReaderEntry readerEntry
Definition: WaveTrack.cpp:721
static const AudacityProject::AttachedObjects::RegisteredFactory key2
Definition: WaveTrack.cpp:4472
DEFINE_XML_METHOD_REGISTRY(WaveTrackIORegistry)
BoolSetting EditClipsCanMove
Definition: WaveTrack.cpp:4547
static auto DefaultName
Definition: WaveTrack.cpp:704
static auto TrackFactoryFactory
Definition: WaveTrack.cpp:4466
void VisitBlocks(TrackList &tracks, BlockVisitor visitor, SampleBlockIDSet *pIDs)
Definition: WaveTrack.cpp:4437
StringSetting AudioTrackNameSetting
Definition: WaveTrack.cpp:4528
void InspectBlocks(const TrackList &tracks, BlockInspector inspector, SampleBlockIDSet *pIDs)
Definition: WaveTrack.cpp:4459
static const Track::TypeInfo & typeInfo()
Definition: WaveTrack.cpp:943
std::function< void(SampleBlock &) > BlockVisitor
Definition: WaveTrack.h:1233
#define WAVETRACK_MERGE_POINT_TOLERANCE
Definition: WaveTrack.h:61
std::unordered_set< SampleBlockID > SampleBlockIDSet
Definition: WaveTrack.h:1231
std::function< void(const SampleBlock &) > BlockInspector
Definition: WaveTrack.h:1234
std::vector< WaveClip * > WaveClipPointers
Definition: WaveTrack.h:49
std::vector< const WaveClip * > WaveClipConstPointers
Definition: WaveTrack.h:50
std::vector< Attribute > AttributesList
Definition: XMLTagHandler.h:40
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Abstraction of a progress dialog with well defined time-to-completion estimate.
Definition: BasicUI.h:157
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:344
double GetEndTime() const
Get the maximum of End() values of intervals, or 0 when none.
Definition: Channel.cpp:135
double GetStartTime() const
Get the minimum of Start() values of intervals, or 0 when none.
Definition: Channel.cpp:124
LinkType
For two tracks describes the type of the linkage.
Definition: Channel.h:571
@ Group
compatibility with projects that were generated by older versions of Audacity
@ Aligned
Tracks are grouped and changes should be synchronized.
double Start() const
Definition: Channel.h:41
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:274
Subclass * Find(const RegisteredFactory &key)
Get a (bare) pointer to an attachment, or null, down-cast it to Subclass *; will not create on demand...
Definition: ClientData.h:341
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:317
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:72
void SetOffset(double newOffset)
Definition: Envelope.cpp:787
void CollapseRegion(double t0, double t1, double sampleDur)
Definition: Envelope.cpp:378
No change to time at all.
Definition: TimeWarper.h:69
bool GetSolo() const
Definition: PlayableTrack.h:48
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
bool GetMute() const
Definition: PlayableTrack.h:47
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
static SampleBlockFactoryPtr New(AudacityProject &project)
Definition: SampleBlock.cpp:16
Two sample formats, remembering format of original source and describing stored format.
Definition: SampleFormat.h:79
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
Definition: Sequence.h:53
size_t GetIdealBlockSize() const
Definition: Sequence.cpp:82
static bool IsValidSampleFormat(const int nValue)
true if nValue is one of the sampleFormat enum values
Definition: Sequence.cpp:1875
bool ReadWithDefault(T *pVar, const T &defaultValue) const
overload of ReadWithDefault returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:211
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:205
A MessageBoxException that shows a given, unvarying string.
Specialization of Setting for strings.
Definition: Prefs.h:368
Transforms one point in time to another point. For example, a time stretching effect might use one to...
Definition: TimeWarper.h:62
virtual double Warp(double originalTime) const =0
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:122
void Notify(bool allChannels, int code=-1)
Definition: Track.cpp:257
TrackList * GetHolder() const
Definition: Track.h:1462
std::shared_ptr< TrackList > GetOwner() const
Definition: Track.h:254
virtual bool LinkConsistencyFix(bool doFix=true)
Check consistency of channel groups, and maybe fix it.
Definition: Track.cpp:299
void SetLinkType(LinkType linkType, bool completeList=true)
Definition: Track.cpp:140
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:160
bool IsLeader() const override
Definition: Track.cpp:291
std::shared_ptr< Track > Holder
Definition: Track.h:225
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
Definition: Track.cpp:1235
virtual TrackListHolder Duplicate() const
public nonvirtual duplication function that invokes Clone()
Definition: Track.cpp:86
ChannelGroupData & GetGroupData()
Definition: Track.cpp:159
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:56
void Init(const Track &orig)
Definition: Track.cpp:50
void OnProjectTempoChange(double newTempo)
method to set project tempo on track
Definition: Track.cpp:1361
LinkType GetLinkType() const noexcept
Definition: Track.cpp:1280
const std::optional< double > & GetProjectTempo() const
Definition: Track.cpp:1369
bool Any() const
Definition: Track.cpp:285
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:975
static TrackListHolder Create(AudacityProject *pOwner)
Definition: Track.cpp:365
size_t NChannels() const
Definition: Track.cpp:960
TrackKind * Add(const std::shared_ptr< TrackKind > &t)
Definition: Track.h:1183
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1079
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1146
static TrackListHolder Temporary(AudacityProject *pProject, const Track::Holder &left={}, const Track::Holder &right={})
Definition: Track.cpp:1418
ChannelSampleView GetSampleView(double t0, double t1, bool mayThrow) const
Request channel samples within [t0, t1), not knowing in advance how many this will be.
Definition: WaveTrack.cpp:3462
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
Definition: WaveTrack.cpp:3060
WaveTrack & GetTrack()
Definition: WaveTrack.h:1202
~WaveChannel() override
bool Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Random-access assignment of a range of samples.
Definition: WaveTrack.cpp:3506
bool Append(constSamplePtr buffer, sampleFormat format, size_t len)
Definition: WaveTrack.cpp:2656
auto Intervals()
Definition: WaveTrack.h:147
float GetRMS(double t0, double t1, bool mayThrow=true) const
Get root-mean-square.
Definition: WaveTrack.cpp:3100
bool AppendBuffer(constSamplePtr buffer, sampleFormat format, size_t len, unsigned stride, sampleFormat effectiveFormat)
Definition: WaveTrack.cpp:2647
WaveClip & mWideClip
Definition: WaveTrack.h:126
double GetStretchRatio() const override
Definition: WaveTrack.cpp:126
constSamplePtr GetAppendBuffer() const
Definition: WaveTrack.cpp:166
size_t GetAppendBufferLen() const
Definition: WaveTrack.cpp:172
double GetTrimLeft() const
Definition: WaveTrack.cpp:131
const Sequence & GetSequence() const
Definition: WaveTrack.cpp:157
double GetTrimRight() const
Definition: WaveTrack.cpp:136
bool Intersects(double t0, double t1) const
Definition: WaveTrack.cpp:72
double GetPlayStartTime() const override
Definition: WaveTrack.cpp:111
sampleCount GetVisibleSampleCount() const override
Definition: WaveTrack.cpp:101
const WaveClip & GetNarrowClip() const
Definition: WaveTrack.h:124
int GetRate() const override
Definition: WaveTrack.cpp:106
int GetColourIndex() const
Definition: WaveTrack.cpp:177
double Start() const
Definition: WaveTrack.cpp:77
~WaveChannelInterval() override
sampleCount TimeToSamples(double time) const override
Definition: WaveTrack.cpp:121
double GetPlayEndTime() const override
Definition: WaveTrack.cpp:116
bool GetSamples(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow=true) const
Definition: WaveTrack.cpp:141
const Envelope & GetEnvelope() const
Definition: WaveTrack.cpp:95
double End() const
Definition: WaveTrack.cpp:82
AudioSegmentSampleView GetSampleView(double t0, double t1, bool mayThrow) const
Request interval samples within [t0, t1). t0 and t1 are truncated to the interval start and end....
Definition: WaveTrack.cpp:88
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:103
double GetStretchRatio() const override
Definition: WaveClip.cpp:340
double GetSequenceStartTime() const noexcept
Definition: WaveClip.cpp:1348
void SetSequenceStartTime(double startTime)
Definition: WaveClip.cpp:1355
sampleCount TimeToSamples(double time) const override
Definition: WaveClip.cpp:1227
void ShiftBy(double delta) noexcept
Definition: WaveClip.cpp:1374
sampleCount GetVisibleSampleCount() const override
Definition: WaveClip.cpp:1299
double GetTrimRight() const noexcept
Returns the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:1320
double GetPlayStartTime() const noexcept override
Definition: WaveClip.cpp:1257
int GetRate() const override
Definition: WaveClip.h:156
double GetTrimLeft() const noexcept
Returns the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:1310
sampleCount GetPlayEndSample() const
Real end time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:1294
constSamplePtr GetAppendBuffer(size_t ii) const
Get one channel of the append buffer.
Definition: WaveClip.cpp:388
double GetPlayEndTime() const override
Definition: WaveClip.cpp:1267
sampleCount GetPlayStartSample() const
Real start time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:1289
void HandleXMLEndTag(const std::string_view &tag) override
Definition: WaveClip.cpp:599
bool SplitsPlayRegion(double t) const
[ < t and t < ), such that if the track were split at t, it would split this clip in two of lengths >...
Definition: WaveClip.cpp:1379
bool GetSamples(size_t ii, samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow=true) const
Get samples from one channel.
Definition: WaveClip.cpp:168
void SetTrimRight(double trim)
Sets the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:1315
Envelope * GetEnvelope()
Definition: WaveClip.h:388
double SamplesToTime(sampleCount s) const noexcept
Definition: WaveClip.cpp:1232
double GetPlayDuration() const
Definition: WaveClip.cpp:1279
bool HasEqualStretchRatio(const WaveClip &other) const
Definition: WaveClip.cpp:349
bool IntersectsPlayRegion(double t0, double t1) const
[t0, t1) ∩ [...) != ∅
Definition: WaveClip.cpp:1403
AudioSegmentSampleView GetSampleView(size_t iChannel, sampleCount start, size_t length, bool mayThrow=true) const override
Request up to length samples. The actual number of samples available from the returned view is querie...
Definition: WaveClip.cpp:145
void SetTrimLeft(double trim)
Sets the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:1305
Sequence * GetSequence(size_t ii)
Definition: WaveClip.h:408
bool Paste(double t0, const WaveClip &other)
Definition: WaveClip.cpp:670
size_t GetAppendBufferLen() const
Definition: WaveClip.cpp:266
int GetColourIndex() const
Definition: WaveClip.h:182
size_t GetWidth() const override
Definition: WaveClip.cpp:163
void Flush()
Flush must be called after last Append.
Definition: WaveClip.cpp:514
std::pair< Iterator, Iterator > Pair
Definition: WaveTrack.h:750
void push(WaveClipHolders &clips)
Definition: WaveTrack.cpp:4427
AllClipsIterator & operator++()
Definition: WaveTrack.cpp:4411
void ForEachClip(const std::function< void(WaveClip &)> &op)
Definition: WaveTrack.cpp:476
double GetTrimRight() const
Definition: WaveTrack.cpp:455
double SamplesToTime(sampleCount s) const
Definition: WaveTrack.cpp:429
double GetTrimLeft() const
Definition: WaveTrack.cpp:449
bool WithinPlayRegion(double t) const
Definition: WaveTrack.cpp:413
double GetPlayStartTime() const
Definition: WaveTrack.cpp:393
double GetPlayEndTime() const
Definition: WaveTrack.cpp:399
void SetTrimRight(double t)
Definition: WaveTrack.cpp:235
void ClearLeft(double t)
Definition: WaveTrack.cpp:241
bool IsPlaceholder() const
Definition: WaveTrack.cpp:461
void SetTrimLeft(double t)
Definition: WaveTrack.cpp:229
double GetStretchRatio() const
Definition: WaveTrack.cpp:418
std::shared_ptr< Interval > GetStretchRenderedCopy(const std::function< void(double)> &reportProgress, const ChannelGroup &group, const SampleBlockFactoryPtr &factory, sampleFormat format)
Definition: WaveTrack.cpp:265
void SetEnvelope(const Envelope &envelope)
Definition: WaveTrack.cpp:471
bool IntersectsPlayRegion(double t0, double t1) const
Definition: WaveTrack.cpp:406
void SetSequenceStartTime(double t)
Definition: WaveTrack.cpp:439
std::shared_ptr< ChannelInterval > DoGetChannel(size_t iChannel) override
Retrieve a channel.
Definition: WaveTrack.cpp:487
const wxString & GetName() const
Definition: WaveTrack.cpp:371
Interval(const ChannelGroup &group, const std::shared_ptr< WaveClip > &pClip, const std::shared_ptr< WaveClip > &pClip1)
Definition: WaveTrack.cpp:182
sampleCount TimeToSamples(double time) const
Definition: WaveTrack.cpp:424
void TrimRightTo(double t)
Definition: WaveTrack.cpp:223
double GetSequenceStartTime() const
Definition: WaveTrack.cpp:434
void SetPlayStartTime(double time)
Definition: WaveTrack.cpp:388
void ClearRight(double t)
Definition: WaveTrack.cpp:247
void SetColorIndex(int index)
Definition: WaveTrack.cpp:377
const Envelope & GetEnvelope() const
Definition: WaveTrack.cpp:466
int GetColorIndex() const
Definition: WaveTrack.cpp:382
bool StretchRatioEquals(double value) const
Definition: WaveTrack.cpp:356
~Interval() override
double GetSequenceEndTime() const
Definition: WaveTrack.cpp:444
void TrimLeftTo(double t)
Definition: WaveTrack.cpp:217
void Append(constSamplePtr buffer[], sampleFormat format, size_t len)
Definition: WaveTrack.cpp:205
void SetName(const wxString &name)
Definition: WaveTrack.cpp:366
void StretchLeftTo(double t)
Definition: WaveTrack.cpp:253
void StretchRightTo(double t)
Definition: WaveTrack.cpp:259
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:1251
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
Definition: WaveTrack.cpp:726
static void Destroy(AudacityProject &project)
Definition: WaveTrack.cpp:4493
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:4476
static WaveTrackFactory & Reset(AudacityProject &project)
Definition: WaveTrack.cpp:4486
A Track that contains audio waveform data.
Definition: WaveTrack.h:222
void HandleXMLEndTag(const std::string_view &tag) override
Definition: WaveTrack.cpp:2853
void FlushOne()
Definition: WaveTrack.cpp:2772
std::pair< size_t, size_t > GetFloatsCenteredAroundTime(double t, size_t iChannel, float *buffer, size_t numSideSamples, bool mayThrow) const
Gets as many samples as it can, but no more than 2 * numSideSamples + 1, centered around t....
Definition: WaveTrack.cpp:3175
SampleBlockFactoryPtr mpFactory
Definition: WaveTrack.h:1191
bool MergeClips(int clipidx1, int clipidx2)
Definition: WaveTrack.cpp:4065
bool GetMute() const override
May vary asynchronously.
Definition: WaveTrack.cpp:2788
bool HasHiddenData() const
Whether any clips have hidden audio.
Definition: WaveTrack.cpp:4382
void SetRate(double newRate)
Definition: WaveTrack.cpp:1090
const WaveClip * GetClipAtTime(double time) const
Definition: WaveTrack.cpp:3718
void DoSetPan(float value)
Definition: WaveTrack.cpp:1135
TrackListHolder Clone() const override
Definition: WaveTrack.cpp:1043
size_t GetWidth() const
The width of every WaveClip in this track; for now always 1.
Definition: WaveTrack.cpp:795
static WaveTrack * New(AudacityProject &project)
Definition: WaveTrack.cpp:767
ChannelGroup & DoGetChannelGroup() const override
Subclass must override.
Definition: WaveTrack.cpp:1028
void SplitDelete(double t0, double t1)
Definition: WaveTrack.cpp:1835
void SplitAt(double t)
Definition: WaveTrack.cpp:3963
std::vector< Region > Regions
Definition: WaveTrack.h:243
void SetFloatsFromTime(double t, size_t iChannel, const float *buffer, size_t numSamples, sampleFormat effectiveFormat, PlaybackDirection direction)
Similar to GetFloatsFromTime, but for writing. Sets as many samples as it can according to the same r...
Definition: WaveTrack.cpp:3290
bool InsertClip(WaveClipHolder clip)
Definition: WaveTrack.cpp:2338
void Silence(double t0, double t1, ProgressReporter reportProgress) override
Definition: WaveTrack.cpp:2408
bool AddClip(const std::shared_ptr< WaveClip > &clip)
Definition: WaveTrack.cpp:1892
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!
Definition: WaveTrack.cpp:3134
WaveClipConstHolders GetClipsIntersecting(double t0, double t1) const
Definition: WaveTrack.cpp:3701
void Reinit(const WaveTrack &orig)
Definition: WaveTrack.cpp:829
bool CanOffsetClips(const std::vector< WaveClip * > &clips, double amount, double *allowedAmount=nullptr)
Decide whether the clips could be offset (and inserted) together without overlapping other clips.
Definition: WaveTrack.cpp:3852
bool CloseLock() noexcept
Should be called upon project close. Not balanced by unlocking calls.
Definition: WaveTrack.cpp:2982
void SetFloatsCenteredAroundTime(double t, size_t iChannel, const float *buffer, size_t numSideSamples, sampleFormat effectiveFormat)
Similar to GetFloatsCenteredAroundTime, but for writing. Sets as many samples as it can according to ...
Definition: WaveTrack.cpp:3278
void CopyClipEnvelopes()
Definition: WaveTrack.cpp:2746
double GetStartTime() const override
Implement WideSampleSequence.
Definition: WaveTrack.cpp:3045
void ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
Definition: WaveTrack.cpp:1226
TrackListHolder Cut(double t0, double t1) override
Create tracks and modify this track.
Definition: WaveTrack.cpp:1260
int mLegacyRate
used only during deserialization
Definition: WaveTrack.h:1109
void InsertSilence(double t, double len) override
Definition: WaveTrack.cpp:2434
auto Channels()
Definition: WaveTrack.h:272
WaveClipPointers SortedClipArray()
Definition: WaveTrack.cpp:4372
Track::Holder PasteInto(AudacityProject &project, TrackList &list) const override
Definition: WaveTrack.cpp:961
void SetFloatsWithinTimeRange(double t0, double t1, size_t iChannel, const std::function< float(double sampleTime)> &producer, sampleFormat effectiveFormat)
Provides a means of setting clip values as a function of time. Included are closest sample to t0 up t...
Definition: WaveTrack.cpp:3321
void WriteXML(XMLWriter &xmlFile) const override
Definition: WaveTrack.cpp:2915
wxString MakeNewClipName() const
Definition: WaveTrack.cpp:1073
static bool ReverseOne(WaveTrack &track, sampleCount start, sampleCount len, const ProgressReport &report={})
Definition: WaveTrack.cpp:4180
size_t NIntervals() const override
used only during deserialization
Definition: WaveTrack.cpp:982
WaveClip * CreateClip(double offset=.0, const wxString &name=wxEmptyString)
Create new clip and add it to this track.
Definition: WaveTrack.cpp:3760
static wxString GetDefaultAudioTrackNamePreference()
Definition: WaveTrack.cpp:708
bool LinkConsistencyFix(bool doFix) override
Check consistency of channel groups, and maybe fix it.
Definition: WaveTrack.cpp:875
static void PasteOne(WaveTrack &track, double t0, const WaveTrack &other, double startTime, double insertDuration, bool merge=true)
Definition: WaveTrack.cpp:2131
int GetWaveColorIndex() const
Definition: WaveTrack.cpp:1171
const WaveClip * GetNextClip(const WaveClip &clip, PlaybackDirection searchDirection) const
Returns clips next to clip in the given direction, or nullptr if there is none.
Definition: WaveTrack.cpp:3688
WaveClip * NewestOrNewClip()
Get access to the most recently added clip, or create a clip, if there is not already one....
Definition: WaveTrack.cpp:3780
IntervalConstHolder GetNextInterval(const Interval &interval, PlaybackDirection searchDirection) const
Definition: WaveTrack.cpp:499
std::optional< TranslatableString > GetErrorOpening() const override
Definition: WaveTrack.cpp:2970
std::shared_ptr<::Channel > DoGetChannel(size_t iChannel) override
Retrieve a channel.
Definition: WaveTrack.cpp:1014
ChannelGroup & ReallyDoGetChannelGroup() const override
This is temporary! It defaults to call the above.
Definition: WaveTrack.cpp:1034
sampleFormat GetSampleFormat() const override
Definition: WaveTrack.cpp:1220
void Join(double t0, double t1, const ProgressReporter &reportProgress)
Definition: WaveTrack.cpp:2565
void ClearAndAddCutLine(double t0, double t1)
Definition: WaveTrack.cpp:1444
void SyncLockAdjust(double oldT1, double newT1) override
Definition: WaveTrack.cpp:2053
void ExpandOneCutLine(double cutLinePosition, double *cutlineStart, double *cutlineEnd)
Definition: WaveTrack.cpp:3996
const TypeInfo & GetTypeInfo() const override
Definition: WaveTrack.cpp:951
std::function< bool(double)> ProgressReport
Definition: WaveTrack.h:915
WaveClip * GetClipByIndex(int index)
Get the nth clip in this WaveTrack (will return nullptr if not found).
Definition: WaveTrack.cpp:3818
WaveTrack(const SampleBlockFactoryPtr &pFactory, sampleFormat format, double rate)
Definition: WaveTrack.cpp:776
float GetPan() const
Definition: WaveTrack.cpp:1130
size_t GetIdealBlockSize()
Definition: WaveTrack.cpp:2721
void ApplyStretchRatio(std::optional< TimeInterval > interval, ProgressReporter reportProgress)
Definition: WaveTrack.cpp:2351
size_t CountBlocks() const
Definition: WaveTrack.cpp:1209
ChannelGroupSampleView GetSampleView(double t0, double t1, bool mayThrow=true) const
Request samples within [t0, t1), not knowing in advance how many this will be.
Definition: WaveTrack.cpp:3451
void ApplyStretchRatioOnIntervals(const std::vector< IntervalHolder > &intervals, const ProgressReporter &reportProgress)
Definition: WaveTrack.cpp:4099
void CreateWideClip(double offset=.0, const wxString &name=wxEmptyString)
Definition: WaveTrack.cpp:3753
static Holder CopyOne(const WaveTrack &track, double t0, double t1, bool forClipboard)
Definition: WaveTrack.cpp:1376
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:1436
void Init(const WaveTrack &orig)
Definition: WaveTrack.cpp:823
void DoSetRate(double newRate)
Definition: WaveTrack.cpp:1100
std::shared_ptr< Interval > IntervalHolder
Definition: WaveTrack.h:1011
bool IsLeader() const override
Definition: WaveTrack.cpp:2778
void Split(double t0, double t1)
Definition: WaveTrack.cpp:3952
void Flush() override
Definition: WaveTrack.cpp:2734
void ExpandCutLine(double cutLinePosition, double *cutlineStart=nullptr, double *cutlineEnd=nullptr)
Definition: WaveTrack.cpp:3984
bool RemoveCutLine(double cutLinePosition)
Remove cut line, without expanding the audio in it.
Definition: WaveTrack.cpp:4049
void DoOnProjectTempoChange(const std::optional< double > &oldTempo, double newTempo) override
Definition: WaveTrack.cpp:857
void ReplaceInterval(const IntervalHolder &oldOne, const IntervalHolder &newOne)
Definition: WaveTrack.cpp:4143
size_t GetFloatsFromTime(double t, size_t iChannel, float *buffer, size_t numSamples, bool mayThrow, PlaybackDirection direction) const
Helper for GetFloatsCenteredAroundTime. If direction == PlaybackDirection::Backward,...
Definition: WaveTrack.cpp:3243
void SetWaveColorIndex(int colorIndex)
Definition: WaveTrack.cpp:1177
sampleCount GetSequenceSamplesCount() const
Definition: WaveTrack.cpp:1197
static void JoinOne(WaveTrack &track, double t0, double t1)
Definition: WaveTrack.cpp:2589
void SetFloatAtTime(double t, size_t iChannel, float value, sampleFormat effectiveFormat)
Sets sample nearest to t to value. Silently fails if GetClipAtTime(t) == nullptr.
Definition: WaveTrack.cpp:3315
bool IsEmpty(double t0, double t1) const
Returns true if there are no WaveClips in the specified region.
Definition: WaveTrack.cpp:1238
sampleFormat WidestEffectiveFormat() const override
Definition: WaveTrack.cpp:3551
void SetPan(float newPan)
Definition: WaveTrack.cpp:1140
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
Definition: WaveTrack.cpp:2865
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
Definition: WaveTrack.cpp:2798
static const TypeInfo & ClassTypeInfo()
Definition: WaveTrack.cpp:956
void Paste(double t0, const Track &src) override
Definition: WaveTrack.cpp:2393
bool MergeOneClipPair(int clipidx1, int clipidx2)
Definition: WaveTrack.cpp:4074
void MoveTo(double o) override
Definition: WaveTrack.cpp:845
void SetLegacyFormat(sampleFormat format)
Definition: WaveTrack.cpp:2741
std::shared_ptr< WaveClip > RemoveAndReturnClip(WaveClip *clip)
Definition: WaveTrack.cpp:1879
void Trim(double t0, double t1)
Definition: WaveTrack.cpp:1287
void Disjoin(double t0, double t1)
Definition: WaveTrack.cpp:2477
void InsertInterval(const IntervalHolder &interval)
Definition: WaveTrack.cpp:4118
TrackListHolder WideEmptyCopy(const SampleBlockFactoryPtr &pFactory={}, bool keepLink=true) const
Definition: WaveTrack.cpp:1342
void DoSetGain(float value)
Definition: WaveTrack.cpp:1117
void Resample(int rate, BasicUI::ProgressDialog *progress=NULL)
Definition: WaveTrack.cpp:4155
static void WriteOneXML(const WaveTrack &track, XMLWriter &xmlFile, size_t iChannel, size_t nChannels)
Definition: WaveTrack.cpp:2926
void SetClipRates(double newRate)
Definition: WaveTrack.cpp:1106
double GetEndTime() const override
Implement WideSampleSequence.
Definition: WaveTrack.cpp:3050
static void ClearAndPasteOne(WaveTrack &track, double t0, double t1, double startTime, double endTime, const WaveTrack &src, bool preserve, bool merge, const TimeWarper *effectWarper, bool clearByTrimming)
Definition: WaveTrack.cpp:1555
float GetGain() const
Definition: WaveTrack.cpp:1112
const WaveClip * FindClipByName(const wxString &name) const
Returns nullptr if clip with such name was not found.
Definition: WaveTrack.cpp:1004
const WaveClip * GetRightmostClip() const
Definition: WaveTrack.cpp:3003
std::shared_ptr< WideChannelGroupInterval > DoGetInterval(size_t iInterval) override
Retrieve an interval.
Definition: WaveTrack.cpp:988
int GetClipIndex(const WaveClip *clip) const
Definition: WaveTrack.cpp:3811
AudioGraph::ChannelType GetChannelType() const override
Classify this channel.
Definition: WaveTrack.cpp:811
bool FormatConsistencyCheck() const
Whether all tracks in group and all clips have a common sample format.
Definition: WaveTrack.cpp:2327
void SetGain(float newGain)
Definition: WaveTrack.cpp:1122
double GetRate() const override
Definition: WaveTrack.cpp:1085
WaveClip * RightmostOrNewClip()
Get access to the last (rightmost) clip, or create a clip, if there is not already one.
Definition: WaveTrack.cpp:3790
void ClearAndPasteAtSameTempo(double t0, double t1, const WaveTrack &src, bool preserve, bool merge, const TimeWarper *effectWarper, bool clearByTrimming)
Definition: WaveTrack.cpp:1518
TrackListHolder Copy(double t0, double t1, bool forClipboard=true) const override
Create new tracks and don't modify this track.
Definition: WaveTrack.cpp:1365
auto Intervals()
Definition: WaveTrack.h:1026
WaveClipHolders & GetClips()
Definition: WaveTrack.h:694
TrackListHolder MonoToStereo()
Definition: WaveTrack.cpp:1355
int GetNumClips() const
Definition: WaveTrack.cpp:3831
bool RateConsistencyCheck() const
Whether all clips of a leader track have a common rate.
Definition: WaveTrack.cpp:2306
bool GetSolo() const override
May vary asynchronously.
Definition: WaveTrack.cpp:2793
bool GetFloatAtTime(double t, size_t iChannel, float &value, bool mayThrow) const
Definition: WaveTrack.cpp:3267
float GetChannelGain(int channel) const override
Takes gain and pan into account.
Definition: WaveTrack.cpp:1153
bool CanInsertClip(const WaveClip &clip, double &slideBy, double tolerance) const
Definition: WaveTrack.cpp:3910
const ChannelGroup * FindChannelGroup() const override
Find associated ChannelGroup if any.
Definition: WaveTrack.cpp:2783
ClipConstHolders GetClipInterfaces() const
Get access to the (visible) clips in the tracks, in unspecified order.
Definition: WaveTrack.cpp:3014
size_t NChannels() const override
May report more than one only when this is a leader track.
Definition: WaveTrack.cpp:800
void DiscardTrimmed()
Remove hidden audio from all clips.
Definition: WaveTrack.cpp:4392
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:2699
void PasteWaveTrackAtSameTempo(double t0, const WaveTrack &other, bool merge)
Definition: WaveTrack.cpp:2112
void GetEnvelopeValues(double *buffer, size_t bufferLen, double t0, bool backwards) const override
Definition: WaveTrack.cpp:3580
static bool ReverseOneClip(WaveTrack &track, sampleCount start, sampleCount len, sampleCount originalStart, sampleCount originalEnd, const ProgressReport &report={})
Definition: WaveTrack.cpp:4308
bool HasTrivialEnvelope() const override
Definition: WaveTrack.cpp:3569
const WaveClip * GetLeftmostClip() const
Definition: WaveTrack.cpp:2992
void PasteWaveTrack(double t0, const WaveTrack &other, bool merge)
Definition: WaveTrack.cpp:2100
double mLegacyProjectFileOffset
Definition: WaveTrack.h:1195
size_t GetBestBlockSize(sampleCount t) const
Definition: WaveTrack.cpp:2679
sampleFormat mLegacyFormat
used only during deserialization
Definition: WaveTrack.h:1110
std::shared_ptr< WaveTrack > Holder
Definition: WaveTrack.h:298
bool Reverse(sampleCount start, sampleCount len, const ProgressReport &report={})
Definition: WaveTrack.cpp:4164
static double ProjectNyquistFrequency(const AudacityProject &project)
Definition: WaveTrack.cpp:696
TrackListHolder DuplicateWithOtherTempo(double newTempo, WaveTrack *&leader) const
Definition: WaveTrack.cpp:867
WaveClipHolders mClips
Definition: WaveTrack.h:1107
void ClearAndPaste(double t0, double t1, const WaveTrack &src, bool preserve=true, bool merge=true, const TimeWarper *effectWarper=nullptr, bool clearByTrimming=false)
Definition: WaveTrack.cpp:1498
const WaveClip * GetAdjacentClip(const WaveClip &clip, PlaybackDirection searchDirection) const
Similar to GetNextClip, but returns nullptr if the neighbour clip is not adjacent.
Definition: WaveTrack.cpp:3656
void RemoveInterval(const IntervalHolder &interval)
Definition: WaveTrack.cpp:4130
sampleCount GetVisibleSampleCount() const
Definition: WaveTrack.cpp:1187
Envelope * GetEnvelopeAtTime(double time)
Definition: WaveTrack.cpp:3740
bool GetOne(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool backwards, fillFormat fill, bool mayThrow, sampleCount *pNumWithinClips) const
Definition: WaveTrack.cpp:3359
Holder EmptyCopy(const SampleBlockFactoryPtr &pFactory={}, bool keepLink=true) const
Definition: WaveTrack.cpp:1323
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1, sampleFormat effectiveFormat=widestSampleFormat, size_t iChannel=0) override
Definition: WaveTrack.cpp:2665
virtual ~WaveTrack()
Definition: WaveTrack.cpp:840
void HandleClear(double t0, double t1, bool addCutLines, bool split, bool clearByTrimming=false)
Definition: WaveTrack.cpp:1909
wxString MakeClipCopyName(const wxString &originalName) const
Definition: WaveTrack.cpp:1061
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=FillFormat::fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
"narrow" overload fetches first channel only
Definition: SampleTrack.h:46
TrackListHolder SplitCut(double t0, double t1)
Definition: WaveTrack.cpp:1272
IntervalHolder GetIntervalAtTime(double t)
Definition: WaveTrack.cpp:528
double LongSamplesToTime(sampleCount pos) const
sampleCount TimeToLongSamples(double t0) const
double SnapToSample(double t) const
static const TypeInfo & ClassTypeInfo()
Definition: SampleTrack.cpp:60
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...
Definition: XMLTagHandler.h:42
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:25
virtual void StartTag(const wxString &name)
Definition: XMLWriter.cpp:79
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:36
virtual void EndTag(const wxString &name)
Definition: XMLWriter.cpp:102
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
double as_double() const
Definition: SampleCount.h:46
#define lrint(dbl)
Definition: float_cast.h:169
ChannelType
Mutually exclusive channel classifications.
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:196
template struct REGISTRIES_API Cloneable<>
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
PROJECT_RATE_API sampleFormat SampleFormatChoice()
double GetRate(const Track &track)
Definition: TimeTrack.cpp:196
void TrimLeftTo(WaveTrack::Interval &interval, double t)
void TrimRightTo(WaveTrack::Interval &interval, double t)
WaveClipHolders::iterator FindClip(WaveClipHolders &list, const WaveClip *clip, int *distance=nullptr)
Definition: WaveTrack.cpp:1863
std::conditional_t< std::is_const_v< std::remove_pointer_t< FloatType > >, constSamplePtr, samplePtr > BufferCharType
Definition: WaveTrack.cpp:3193
void RoundToNearestClipSample(const WaveTrack &track, double &t)
Definition: WaveTrack.cpp:3165
bool AreAligned(const WaveClipPointers &a, const WaveClipPointers &b)
Definition: WaveTrack.cpp:666
ProjectFormatExtensionsRegistry::Extension smartClipsExtension([](const AudacityProject &project) -> ProjectFormatVersion { const TrackList &trackList=TrackList::Get(project);for(auto wt :trackList.Any< const WaveTrack >()) for(const auto pChannel :TrackList::Channels(wt)) for(const auto &clip :pChannel->GetAllClips()) if(clip->GetTrimLeft() > 0.0||clip->GetTrimRight() > 0.0) return { 3, 1, 0, 0 };return BaseProjectFormatVersion;})
SampleAccessArgs< BufferType > GetSampleAccessArgs(const WaveClip &clip, double startOrEndTime, BufferType buffer, size_t totalToRead, size_t alreadyRead, bool forward)
Definition: WaveTrack.cpp:3203
static const ChannelGroup::Attachments::RegisteredFactory waveTrackDataFactory
Definition: WaveTrack.cpp:578
ProjectFormatExtensionsRegistry::Extension stretchedClipsExtension([](const AudacityProject &project) -> ProjectFormatVersion { const TrackList &trackList=TrackList::Get(project);for(auto wt :trackList.Any< const WaveTrack >()) for(const auto pChannel :TrackList::Channels(wt)) for(const auto &clip :pChannel->GetAllClips()) if(clip->GetStretchRatio() !=1.0) return { 3, 4, 0, 0 };return BaseProjectFormatVersion;})
Track::LinkType ToLinkType(int value)
Definition: WaveTrack.cpp:685
Cont1 FillSortedClipArray(const Cont2 &mClips)
Definition: WaveTrack.cpp:4360
static RegisteredToolbarFactory factory
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
Definition: fast_float.h:2512
__finl float_x4 __vecc sqrt(const float_x4 &a)
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40
STL namespace.
float *const * Get() const
A convenient base class defining abstract virtual Clone() for a given kind of pointer.
Definition: ClientData.h:49
"finally" as in The C++ Programming Language, 4th ed., p. 358 Useful for defining ad-hoc RAII actions...
Definition: MemoryX.h:173
A struct to register the project extension that requires a different project version.
A structure that holds the project version.
Empty argument passed to some public constructors.
Definition: Track.h:129
const BufferCharType< BufferType > offsetBuffer
Definition: WaveTrack.cpp:3197
WaveTrackData & operator=(const WaveTrackData &)=delete