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
32
33#include "WaveClip.h"
34
35#include <wx/defs.h>
36#include <wx/debug.h>
37#include <wx/log.h>
38
39#include <float.h>
40#include <math.h>
41#include <algorithm>
42#include <optional>
43#include <numeric>
44
45#include "float_cast.h"
46
47#include "Envelope.h"
48#include "Sequence.h"
49
50#include "Project.h"
51#include "ProjectRate.h"
52
53#include "Prefs.h"
54#include "SyncLock.h"
55#include "TimeWarper.h"
56#include "QualitySettings.h"
57
59
61
62using std::max;
63
64namespace {
66 GainAndPan() = default;
68 GainAndPan& operator=(const GainAndPan &) = delete;
69 ~GainAndPan() override;
70 std::unique_ptr<ClientData::Cloneable<>> Clone() const override;
71
72 static GainAndPan &Get(WaveTrack &track);
73 static const GainAndPan &Get(const WaveTrack &track);
74
75 float GetGain() const;
76 void SetGain(float value);
77 float GetPan() const;
78 void SetPan(float value);
79
80private:
82 std::atomic<float> mGain{ 1.0f };
84 std::atomic<float> mPan{ 0.0f };
85};
86
88gainAndPanFactory{ [](auto &) { return std::make_unique<GainAndPan>(); } };
89
91GainAndPan::GainAndPan(const GainAndPan &other) {
92 SetGain(other.GetGain());
93 SetPan(other.GetPan());
94}
95
96GainAndPan::~GainAndPan() = default;
97
98std::unique_ptr<ClientData::Cloneable<>> GainAndPan::Clone() const {
99 return std::make_unique<GainAndPan>(*this);
100}
101
103 return track.GetGroupData().Track::ChannelGroupAttachments
105}
106
108{
109 return Get(const_cast<WaveTrack &>(track));
110}
111
112float GainAndPan::GetGain() const
113{
114 return mGain.load(std::memory_order_relaxed);
115}
116
117void GainAndPan::SetGain(float value)
118{
119 mGain.store(value, std::memory_order_relaxed);
120}
121
122float GainAndPan::GetPan() const
123{
124 return mPan.load(std::memory_order_relaxed);
125}
126
127void GainAndPan::SetPan(float value)
128{
129 mPan.store(value, std::memory_order_relaxed);
130}
131
133{
134 if (a.size() != b.size())
135 return false;
136
137 const auto compare = [](const WaveClip* a, const WaveClip* b) {
138 // clips are aligned if both sequence start/end
139 // points and play start/end points of the first clip match
140 // the corresponding points of the other clip
141 return a->GetPlayStartTime() == b->GetPlayStartTime() &&
142 a->GetSequenceStartTime() == b->GetSequenceStartTime() &&
143 a->GetPlayEndTime() == b->GetPlayEndTime() &&
144 a->GetSequenceEndTime() == b->GetSequenceEndTime();
145 };
146
147 return std::mismatch(a.begin(), a.end(), b.begin(), compare).first == a.end();
148}
149
150//Handles possible future file values
152{
153 if (value < 0)
155 else if (value > 3)
157 return static_cast<Track::LinkType>(value);
158}
159
160}
161
162static auto DefaultName = XO("Audio");
163
165{
167
168 if (name.empty() || ( name == DefaultName.MSGID() ))
169 // When nothing was specified,
170 // the default-default is whatever translation of...
171 /* i18n-hint: The default name for an audio track. */
172 return DefaultName.Translation();
173 else
174 return name;
175}
176
178 "wavetrack",
180};
181
182std::shared_ptr<WaveTrack> WaveTrackFactory::Create()
183{
185}
186
187std::shared_ptr<WaveTrack> WaveTrackFactory::Create(sampleFormat format, double rate)
188{
189 return std::make_shared<WaveTrack>(mpFactory, format, rate);
190}
191
193{
194 auto &trackFactory = WaveTrackFactory::Get( project );
195 auto &tracks = TrackList::Get( project );
196 auto result = tracks.Add(trackFactory.Create());
197 result->AttachedTrackObjects::BuildAll();
198 return result;
199}
200
202 sampleFormat format, double rate )
204 , mpFactory(pFactory)
205{
207
208 mFormat = format;
209 mRate = (int) rate;
210 mWaveColorIndex = 0;
211}
212
214 : WritableSampleTrack(orig, std::move(a))
215 , mpFactory( orig.mpFactory )
216{
218 for (const auto &clip : orig.mClips)
219 mClips.push_back
220 ( std::make_unique<WaveClip>( *clip, mpFactory, true ) );
221}
222
223// Copy the track metadata but not the contents.
224void WaveTrack::Init(const WaveTrack &orig)
225{
227 mpFactory = orig.mpFactory;
228
229 mFormat = orig.mFormat;
231 mRate = orig.mRate;
232}
233
235{
236 Init(orig);
237
238 // Copy attached data from orig. Nullify data in this where orig had null.
239 Attachments &attachments = *this;
240 attachments = orig;
241}
242
243void WaveTrack::Merge(const Track &orig)
244{
245 orig.TypeSwitch( [&](const WaveTrack *pwt) {
246 const WaveTrack &wt = *pwt;
247 // Copy attached data from orig. Nullify data in this where orig had null.
248 Attachments &attachments = *this;
249 attachments = *pwt;
250 });
251}
252
254{
255}
256
258{
259 return GetStartTime();
260}
261
264{
265 double delta = o - GetOffset();
266
267 for (const auto &clip : mClips)
268 // assume No-fail-guarantee
269 clip->Offset(delta);
270
271 mOffset = o;
272}
273
274bool WaveTrack::LinkConsistencyFix(bool doFix, bool completeList)
275{
276 auto err = !WritableSampleTrack::LinkConsistencyFix(doFix, completeList);
277 if (completeList) {
278 auto linkType = GetLinkType();
279 if (static_cast<int>(linkType) == 1 || //Comes from old audacity version
280 linkType == LinkType::Aligned) {
281 auto next =
282 dynamic_cast<WaveTrack*>(*std::next(GetOwner()->Find(this)));
283 if (next == nullptr) {
284 //next track is absent or not a wave track, fix and report error
285 if (doFix) {
286 wxLogWarning(L"Right track %s is expected to be a WaveTrack."
287 "\n Removing link from left wave track %s.",
288 next->GetName(), GetName());
290 }
291 err = true;
292 }
293 else if (doFix) {
294 auto newLinkType =
295 AreAligned(SortedClipArray(), next->SortedClipArray())
297 //not an error
298 if (newLinkType != linkType)
299 SetLinkType(newLinkType);
300 }
301 }
302 }
303 return !err;
304}
305
306
308{
309 static const Track::TypeInfo info{
310 { "wave", "wave", XO("Wave Track") },
312 return info;
313}
314
315auto WaveTrack::GetTypeInfo() const -> const TypeInfo &
316{
317 return typeInfo();
318}
319
321{
322 return typeInfo();
323}
324
325template< typename Container >
326static Container MakeIntervals(const std::vector<WaveClipHolder> &clips)
327{
328 Container result;
329 for (const auto &clip: clips) {
330 result.emplace_back( clip->GetPlayStartTime(), clip->GetPlayEndTime(),
331 std::make_unique<WaveTrack::IntervalData>( clip ) );
332 }
333 return result;
334}
335
337{
338 auto &trackFactory = WaveTrackFactory::Get( project );
339 auto &pSampleBlockFactory = trackFactory.GetSampleBlockFactory();
340 auto pNewTrack = EmptyCopy( pSampleBlockFactory );
341 pNewTrack->Paste(0.0, this);
342 return pNewTrack;
343}
344
346{
347 return MakeIntervals<ConstIntervals>( mClips );
348}
349
351{
352 return MakeIntervals<Intervals>( mClips );
353}
354
355const WaveClip* WaveTrack::FindClipByName(const wxString& name) const
356{
357 for (const auto& clip : mClips)
358 {
359 if (clip->GetName() == name)
360 return clip.get();
361 }
362 return nullptr;
363}
364
366{
367 auto result = std::make_shared<WaveTrack>(*this, ProtectedCreationArg{});
368 result->Init(*this);
369 return result;
370}
371
372wxString WaveTrack::MakeClipCopyName(const wxString& originalName) const
373{
374 auto name = originalName;
375 for (auto i = 1;; ++i)
376 {
377 if (FindClipByName(name) == nullptr)
378 return name;
379 //i18n-hint Template for clip name generation on copy-paste
380 name = XC("%s.%i", "clip name template").Format(originalName, i).Translation();
381 }
382}
383
385{
386 auto name = GetName();
387 for (auto i = 1;; ++i)
388 {
389 if (FindClipByName(name) == nullptr)
390 return name;
391 //i18n-hint Template for clip name generation on inserting new empty clip
392 name = XC("%s %i", "clip name template").Format(GetName(), i).Translation();
393 }
394}
395
396double WaveTrack::GetRate() const
397{
398 return mRate;
399}
400
401void WaveTrack::SetRate(double newRate)
402{
403 wxASSERT( newRate > 0 );
404 newRate = std::max( 1.0, newRate );
405 auto ratio = mRate / newRate;
406 mRate = (int) newRate;
407 for (const auto &clip : mClips) {
408 clip->SetRate((int)newRate);
409 clip->SetSequenceStartTime( clip->GetSequenceStartTime() * ratio );
410 }
411}
412
414{
415 return GainAndPan::Get(*this).GetGain();
416}
417
418void WaveTrack::DoSetGain(float value)
419{
420 GainAndPan::Get(*this).SetGain(value);
421}
422
423void WaveTrack::SetGain(float newGain)
424{
425 if (GetGain() != newGain) {
426 DoSetGain(newGain);
427 Notify(true);
428 }
429}
430
431float WaveTrack::GetPan() const
432{
433 return GainAndPan::Get(*this).GetPan();
434}
435
436void WaveTrack::DoSetPan(float value)
437{
438 GainAndPan::Get(*this).SetPan(value);
439}
440
441void WaveTrack::SetPan(float newPan)
442{
443 if (newPan > 1.0)
444 newPan = 1.0;
445 else if (newPan < -1.0)
446 newPan = -1.0;
447
448 if ( GetPan() != newPan ) {
449 DoSetPan(newPan);
450 Notify(true);
451 }
452}
453
454float WaveTrack::GetChannelGain(int channel) const
455{
456 float left = 1.0;
457 float right = 1.0;
458
459 const auto pan = GetPan();
460 if (pan < 0)
461 right = (pan + 1.0);
462 else if (pan > 0)
463 left = 1.0 - pan;
464
465 const auto gain = GetGain();
466 if ((channel%2) == 0)
467 return left * gain;
468 else
469 return right * gain;
470}
471
474{
475 for (const auto &clip : mClips)
476 clip->SetColourIndex( colorIndex );
477 mWaveColorIndex = colorIndex;
478}
479
481{
482 sampleCount result{ 0 };
483
484 for (const auto& clip : mClips)
485 result += clip->GetPlaySamplesCount();
486
487 return result;
488}
489
491{
492 sampleCount result{ 0 };
493
494 for (const auto& clip : mClips)
495 result += clip->GetSequenceSamplesCount();
496
497 return result;
498}
499
502 const std::function<void(size_t)> & progressReport)
503{
504 for (const auto& clip : mClips)
505 clip->ConvertToSampleFormat(format, progressReport);
506 mFormat = format;
507}
508
509
510bool WaveTrack::IsEmpty(double t0, double t1) const
511{
512 if (t0 > t1)
513 return true;
514
515 //wxPrintf("Searching for overlap in %.6f...%.6f\n", t0, t1);
516 for (const auto &clip : mClips)
517 {
518 if (!clip->BeforePlayStartTime(t1) && !clip->AfterPlayEndTime(t0)) {
519 //wxPrintf("Overlapping clip: %.6f...%.6f\n",
520 // clip->GetStartTime(),
521 // clip->GetEndTime());
522 // We found a clip that overlaps this region
523 return false;
524 }
525 }
526 //wxPrintf("No overlap found\n");
527
528 // Otherwise, no clips overlap this region
529 return true;
530}
531
532Track::Holder WaveTrack::Cut(double t0, double t1)
533{
534 if (t1 < t0)
536
537 auto tmp = Copy(t0, t1);
538
539 Clear(t0, t1);
540
541 return tmp;
542}
543
546{
547 if (t1 < t0)
549
550 // SplitCut is the same as 'Copy', then 'SplitDelete'
551 auto tmp = Copy(t0, t1);
552
553 SplitDelete(t0, t1);
554
555 return tmp;
556}
557
558#if 0
559Track::Holder WaveTrack::CutAndAddCutLine(double t0, double t1)
560{
561 if (t1 < t0)
563
564 // Cut is the same as 'Copy', then 'Delete'
565 auto tmp = Copy(t0, t1);
566
567 ClearAndAddCutLine(t0, t1);
568
569 return tmp;
570}
571#endif
572
573
574
575//Trim trims within a clip, rather than trimming everything.
576//If a bound is outside a clip, it trims everything.
578void WaveTrack::Trim (double t0, double t1)
579{
580 bool inside0 = false;
581 bool inside1 = false;
582
583 for (const auto &clip : mClips)
584 {
585 if(t1 > clip->GetPlayStartTime() && t1 < clip->GetPlayEndTime())
586 {
587 clip->SetTrimRight(clip->GetTrimRight() + clip->GetPlayEndTime() - t1);
588 inside1 = true;
589 }
590
591 if(t0 > clip->GetPlayStartTime() && t0 < clip->GetPlayEndTime())
592 {
593 clip->SetTrimLeft(clip->GetTrimLeft() + t0 - clip->GetPlayStartTime());
594 inside0 = true;
595 }
596 }
597
598 //if inside0 is false, then the left selector was between
599 //clips, so DELETE everything to its left.
600 if(!inside1 && t1 < GetEndTime())
601 Clear(t1,GetEndTime());
602
603 if(!inside0 && t0 > GetStartTime())
605}
606
607
608
609
611 const SampleBlockFactoryPtr &pFactory, bool keepLink) const
612{
613 auto result = std::make_shared<WaveTrack>( pFactory, mFormat, mRate );
614 result->Init(*this);
615 result->mpFactory = pFactory ? pFactory : mpFactory;
616 if (!keepLink)
617 result->SetLinkType(LinkType::None);
618 return result;
619}
620
621Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const
622{
623 if (t1 < t0)
625
626 auto result = EmptyCopy();
627 WaveTrack *newTrack = result.get();
628
629 // PRL: Why shouldn't cutlines be copied and pasted too? I don't know, but
630 // that was the old behavior. But this function is also used by the
631 // Duplicate command and I changed its behavior in that case.
632
633 for (const auto &clip : mClips)
634 {
635 if (t0 <= clip->GetPlayStartTime() && t1 >= clip->GetPlayEndTime())
636 {
637 // Whole clip is in copy region
638 //wxPrintf("copy: clip %i is in copy region\n", (int)clip);
639
640 newTrack->mClips.push_back
641 (std::make_unique<WaveClip>(*clip, mpFactory, ! forClipboard));
642 WaveClip *const newClip = newTrack->mClips.back().get();
643 newClip->Offset(-t0);
644 }
645 else if (clip->CountSamples(t0, t1) >= 1)
646 {
647 // Clip is affected by command
648 //wxPrintf("copy: clip %i is affected by command\n", (int)clip);
649
650 auto newClip = std::make_unique<WaveClip>
651 (*clip, mpFactory, ! forClipboard, t0, t1);
652 newClip->SetName(clip->GetName());
653
654 newClip->Offset(-t0);
655 if (newClip->GetPlayStartTime() < 0)
656 newClip->SetPlayStartTime(0);
657
658 newTrack->mClips.push_back(std::move(newClip)); // transfer ownership
659 }
660 }
661
662 // AWD, Oct 2009: If the selection ends in whitespace, create a placeholder
663 // clip representing that whitespace
664 // PRL: Only if we want the track for pasting into other tracks. Not if it
665 // goes directly into a project as in the Duplicate command.
666 if (forClipboard &&
667 newTrack->GetEndTime() + 1.0 / newTrack->GetRate() < t1 - t0)
668 {
669 auto placeholder = std::make_unique<WaveClip>(mpFactory,
670 newTrack->GetSampleFormat(),
671 static_cast<int>(newTrack->GetRate()),
672 0 /*colourindex*/);
673 placeholder->SetIsPlaceholder(true);
674 placeholder->InsertSilence(0, (t1 - t0) - newTrack->GetEndTime());
675 placeholder->Offset(newTrack->GetEndTime());
676 newTrack->mClips.push_back(std::move(placeholder)); // transfer ownership
677 }
678
679 return result;
680}
681
683{
684 return Copy(t0, t1);
685}
686
688void WaveTrack::Clear(double t0, double t1)
689{
690 HandleClear(t0, t1, false, false);
691}
692
694void WaveTrack::ClearAndAddCutLine(double t0, double t1)
695{
696 HandleClear(t0, t1, true, false);
697}
698
699namespace {
700
701 //Internal structure, which is supposed to contain
702 //data related to clip boundaries, and used during
703 //ClearAndPaste to restore old splits after new
704 //data is pasted
706 {
707 //Time, where boundary is located
708 double time;
709 //Contains trimmed data, which should be re-appended to
710 //the clip to the left from the boundary, may be null
711 std::shared_ptr<WaveClip> left;
712 //Contains trimmed data, which should be re-appended to
713 //the clip to the right from the boundary, may be null
714 std::shared_ptr<WaveClip> right;
715 //Contains clip name next to the left from the boundary,
716 //if present, that needs to be re-assigned to the matching
717 //clip after split
718 std::optional<wxString> leftClipName;
719 //Contains clip name next to the right from the boundary,
720 //if present, that needs to be re-assigned to the matching
721 //clip after split
722 std::optional<wxString> rightClipName;
723 };
724
725}
726
727//
728// ClearAndPaste() is a specialized version of HandleClear()
729// followed by Paste() and is used mostly by effects that
730// can't replace track data directly using Get()/Set().
731//
732// HandleClear() removes any cut/split lines with the
733// cleared range, but, in most cases, effects want to preserve
734// the existing cut/split lines, trimmed data and clip names,
735// so they are saved before the HandleClear()/Paste() and restored after.
736// When pasted track has split lines with hidden data at same places
737// as the target one, then only targets hidden data is preserved, and
738// hidden data from pasted track is discarded.
739//
740// If the pasted track overlaps two or more clips, then it will
741// be pasted with visible split lines. Normally, effects do not
742// want these extra lines, so they may be merged out.
743//
746void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
747 double t1, // End of time to clear
748 const Track *src, // What to paste
749 bool preserve, // Whether to reinsert splits/cuts
750 bool merge, // Whether to remove 'extra' splits
751 const TimeWarper *effectWarper // How does time change
752 )
753{
754 double dur = std::min(t1 - t0, src->GetEndTime());
755
756 // If duration is 0, then it's just a plain paste
757 if (dur == 0.0) {
758 // use Weak-guarantee
759 Paste(t0, src);
760 return;
761 }
762
763 std::vector<SplitInfo> splits;
764 WaveClipHolders cuts;
765
766 //helper routine, that finds SplitInfo by time value,
767 //or creates a new one if no one exists yet
768 auto get_split = [&](double time) {
769 auto it = std::find_if(splits.begin(), splits.end(), [time](const SplitInfo& split) {
770 return split.time == time;
771 });
772 if(it == splits.end())
773 it = splits.insert(
774 splits.end(),
775 { time, nullptr, nullptr, std::nullopt, std::nullopt }
776 );
777 return it;
778 };
779
780 // If provided time warper was NULL, use a default one that does nothing
781 IdentityTimeWarper localWarper;
782 const TimeWarper *warper = (effectWarper ? effectWarper : &localWarper);
783
784 // Align to a sample
787
788 // Save the cut/split lines whether preserving or not since merging
789 // needs to know if a clip boundary is being crossed since Paste()
790 // will add split lines around the pasted clip if so.
791 for (const auto &clip : mClips) {
792 double st;
793
794 // Remember clip boundaries as locations to split
795 // we need to copy clips, trims and names, because the original ones
796 // could be changed later during Clear/Paste routines
797 st = LongSamplesToTime(TimeToLongSamples(clip->GetPlayStartTime()));
798 if (st >= t0 && st <= t1) {
799 auto it = get_split(st);
800 if (clip->GetTrimLeft() != 0)
801 {
802 //keep only hidden left part
803 it->right = std::make_shared<WaveClip>(*clip, mpFactory, false);
804 it->right->SetTrimLeft(.0);
805 it->right->ClearRight(clip->GetPlayStartTime());
806 }
807 it->rightClipName = clip->GetName();
808 }
809
810 st = LongSamplesToTime(TimeToLongSamples(clip->GetPlayEndTime()));
811 if (st >= t0 && st <= t1) {
812 auto it = get_split(st);
813 if (clip->GetTrimRight() != 0)
814 {
815 //keep only hidden right part
816 it->left = std::make_shared<WaveClip>(*clip, mpFactory, false);
817 it->left->SetTrimRight(.0);
818 it->left->ClearLeft(clip->GetPlayEndTime());
819 }
820 it->leftClipName = clip->GetName();
821 }
822
823 // Search for cut lines
824 auto &cutlines = clip->GetCutLines();
825 // May erase from cutlines, so don't use range-for
826 for (auto it = cutlines.begin(); it != cutlines.end(); ) {
827 WaveClip *cut = it->get();
828 double cs = LongSamplesToTime(TimeToLongSamples(clip->GetSequenceStartTime() +
829 cut->GetSequenceStartTime()));
830
831 // Remember cut point
832 if (cs >= t0 && cs <= t1) {
833
834 // Remember the absolute offset and add to our cuts array.
835 cut->SetSequenceStartTime(cs);
836 cuts.push_back(std::move(*it)); // transfer ownership!
837 it = cutlines.erase(it);
838 }
839 else
840 ++it;
841 }
842 }
843
844 const auto tolerance = 2.0 / GetRate();
845
846 // Now, clear the selection
847 HandleClear(t0, t1, false, false);
848 {
849
850 // And paste in the NEW data
851 Paste(t0, src);
852 {
853 // First, merge the NEW clip(s) in with the existing clips
854 if (merge && splits.size() > 0)
855 {
856 // Now t1 represents the absolute end of the pasted data.
857 t1 = t0 + src->GetEndTime();
858
859 // Get a sorted array of the clips
860 auto clips = SortedClipArray();
861
862 // Scan the sorted clips for the first clip whose start time
863 // exceeds the pasted regions end time.
864 {
865 WaveClip *prev = nullptr;
866 for (const auto clip : clips) {
867 // Merge this clip and the previous clip if the end time
868 // falls within it and this isn't the first clip in the track.
869 if (fabs(t1 - clip->GetPlayStartTime()) < tolerance) {
870 if (prev)
872 break;
873 }
874 prev = clip;
875 }
876 }
877 }
878
879 // Refill the array since clips have changed.
880 auto clips = SortedClipArray();
881
882 {
883 // Scan the sorted clips to look for the start of the pasted
884 // region.
885 WaveClip *prev = nullptr;
886 for (const auto clip : clips) {
887 if (prev) {
888 // It must be that clip is what was pasted and it begins where
889 // prev ends.
890 // use Weak-guarantee
892 break;
893 }
894 if (fabs(t0 - clip->GetPlayEndTime()) < tolerance)
895 // Merge this clip and the next clip if the start time
896 // falls within it and this isn't the last clip in the track.
897 prev = clip;
898 else
899 prev = nullptr;
900 }
901 }
902 }
903
904 // Restore cut/split lines
905 if (preserve) {
906
907 auto attachLeft = [](WaveClip* target, WaveClip* src)
908 {
909 wxASSERT(target->GetTrimLeft() == 0);
910 if (target->GetTrimLeft() != 0)
911 return;
912
913 auto trim = src->GetPlayEndTime() - src->GetPlayStartTime();
914 target->Paste(target->GetPlayStartTime(), src);
915 target->SetTrimLeft(trim);
916 //Play start time needs to be adjusted after
917 //prepending data to the sequence
918 target->Offset(-trim);
919 };
920
921 auto attachRight = [](WaveClip* target, WaveClip* src)
922 {
923 wxASSERT(target->GetTrimRight() == 0);
924 if (target->GetTrimRight() != 0)
925 return;
926
927 auto trim = src->GetPlayEndTime() - src->GetPlayStartTime();
928 target->Paste(target->GetPlayEndTime(), src);
929 target->SetTrimRight(trim);
930 };
931
932 // Restore the split lines and trims, transforming the position appropriately
933 for (const auto& split: splits) {
934 auto at = LongSamplesToTime(TimeToLongSamples(warper->Warp(split.time)));
935 for (const auto& clip : GetClips())
936 {
937 if (clip->WithinPlayRegion(at))//strictly inside
938 {
939 auto newClip = std::make_unique<WaveClip>(*clip, mpFactory, true);
940
941 clip->ClearRight(at);
942 newClip->ClearLeft(at);
943 if (split.left)
944 attachRight(clip.get(), split.left.get());
945 if (split.right)
946 attachLeft(newClip.get(), split.right.get());
947 AddClip(std::move(newClip));
948 break;
949 }
950 else if (clip->GetPlayStartSample() == TimeToLongSamples(at) && split.right)
951 {
952 attachLeft(clip.get(), split.right.get());
953 break;
954 }
955 else if (clip->GetPlayEndSample() == TimeToLongSamples(at) && split.left)
956 {
957 attachRight(clip.get(), split.left.get());
958 break;
959 }
960 }
961 }
962
963 //Restore clip names
964 for (const auto& split : splits)
965 {
966 auto s = TimeToLongSamples(warper->Warp(split.time));
967 for (auto& clip : GetClips())
968 {
969 if (split.rightClipName.has_value() && clip->GetPlayStartSample() == s)
970 clip->SetName(*split.rightClipName);
971 else if (split.leftClipName.has_value() && clip->GetPlayEndSample() == s)
972 clip->SetName(*split.leftClipName);
973 }
974 }
975
976 // Restore the saved cut lines, also transforming if time altered
977 for (const auto &clip : mClips) {
978 double st;
979 double et;
980
981 st = clip->GetPlayStartTime();
982 et = clip->GetPlayEndTime();
983
984 // Scan the cuts for any that live within this clip
985 for (auto it = cuts.begin(); it != cuts.end();) {
986 WaveClip *cut = it->get();
987 //cutlines in this array were orphaned previously
988 double cs = cut->GetSequenceStartTime();
989
990 // Offset the cut from the start of the clip and add it to
991 // this clips cutlines.
992 if (cs >= st && cs <= et) {
993 cut->SetSequenceStartTime(warper->Warp(cs) - st);
994 clip->GetCutLines().push_back( std::move(*it) ); // transfer ownership!
995 it = cuts.erase(it);
996 }
997 else
998 ++it;
999 }
1000 }
1001 }
1002 }
1003}
1004
1006void WaveTrack::SplitDelete(double t0, double t1)
1007{
1008 bool addCutLines = false;
1009 bool split = true;
1010 HandleClear(t0, t1, addCutLines, split);
1011}
1012
1013namespace
1014{
1015 WaveClipHolders::const_iterator
1016 FindClip(const WaveClipHolders &list, const WaveClip *clip, int *distance = nullptr)
1017 {
1018 if (distance)
1019 *distance = 0;
1020 auto it = list.begin();
1021 for (const auto end = list.end(); it != end; ++it)
1022 {
1023 if (it->get() == clip)
1024 break;
1025 if (distance)
1026 ++*distance;
1027 }
1028 return it;
1029 }
1030
1031 WaveClipHolders::iterator
1032 FindClip(WaveClipHolders &list, const WaveClip *clip, int *distance = nullptr)
1033 {
1034 if (distance)
1035 *distance = 0;
1036 auto it = list.begin();
1037 for (const auto end = list.end(); it != end; ++it)
1038 {
1039 if (it->get() == clip)
1040 break;
1041 if (distance)
1042 ++*distance;
1043 }
1044 return it;
1045 }
1046}
1047
1048std::shared_ptr<WaveClip> WaveTrack::RemoveAndReturnClip(WaveClip* clip)
1049{
1050 // Be clear about who owns the clip!!
1051 auto it = FindClip(mClips, clip);
1052 if (it != mClips.end()) {
1053 auto result = std::move(*it); // Array stops owning the clip, before we shrink it
1054 mClips.erase(it);
1055 return result;
1056 }
1057 else
1058 return {};
1059}
1060
1061bool WaveTrack::AddClip(const std::shared_ptr<WaveClip> &clip)
1062{
1063 if (clip->GetSequence()->GetFactory() != this->mpFactory)
1064 return false;
1065
1066 // Uncomment the following line after we correct the problem of zero-length clips
1067 //if (CanInsertClip(clip))
1068 mClips.push_back(clip); // transfer ownership
1069
1070 return true;
1071}
1072
1074void WaveTrack::HandleClear(double t0, double t1,
1075 bool addCutLines, bool split)
1076{
1077 // For debugging, use an ASSERT so that we stop
1078 // closer to the problem.
1079 wxASSERT( t1 >= t0 );
1080 if (t1 < t0)
1082
1083 bool editClipCanMove = GetEditClipsCanMove();
1084
1085 WaveClipPointers clipsToDelete;
1086 WaveClipHolders clipsToAdd;
1087
1088 // We only add cut lines when deleting in the middle of a single clip
1089 // The cut line code is not really prepared to handle other situations
1090 if (addCutLines)
1091 {
1092 for (const auto &clip : mClips)
1093 {
1094 if (!clip->BeforePlayStartTime(t1) && !clip->AfterPlayEndTime(t0) &&
1095 (clip->BeforePlayStartTime(t0) || clip->AfterPlayEndTime(t1)))
1096 {
1097 addCutLines = false;
1098 break;
1099 }
1100 }
1101 }
1102
1103 for (const auto &clip : mClips)
1104 {
1105 if (clip->BeforePlayStartTime(t0) && clip->AfterPlayEndTime(t1))
1106 {
1107 // Whole clip must be deleted - remember this
1108 clipsToDelete.push_back(clip.get());
1109 }
1110 else if (!clip->BeforePlayStartTime(t1) && !clip->AfterPlayEndTime(t0))
1111 {
1112 // Clip data is affected by command
1113 if (addCutLines)
1114 {
1115 // Don't modify this clip in place, because we want a strong
1116 // guarantee, and might modify another clip
1117 clipsToDelete.push_back( clip.get() );
1118 auto newClip = std::make_unique<WaveClip>( *clip, mpFactory, true );
1119 newClip->ClearAndAddCutLine( t0, t1 );
1120 clipsToAdd.push_back( std::move( newClip ) );
1121 }
1122 else
1123 {
1124 if (split) {
1125 // Three cases:
1126
1127 if (clip->BeforePlayStartTime(t0)) {
1128 // Delete from the left edge
1129
1130 // Don't modify this clip in place, because we want a strong
1131 // guarantee, and might modify another clip
1132 clipsToDelete.push_back( clip.get() );
1133 auto newClip = std::make_unique<WaveClip>( *clip, mpFactory, true );
1134 newClip->TrimLeft(t1 - clip->GetPlayStartTime());
1135 clipsToAdd.push_back( std::move( newClip ) );
1136 }
1137 else if (clip->AfterPlayEndTime(t1)) {
1138 // Delete to right edge
1139
1140 // Don't modify this clip in place, because we want a strong
1141 // guarantee, and might modify another clip
1142 clipsToDelete.push_back( clip.get() );
1143 auto newClip = std::make_unique<WaveClip>( *clip, mpFactory, true );
1144 newClip->TrimRight(clip->GetPlayEndTime() - t0);
1145
1146 clipsToAdd.push_back( std::move( newClip ) );
1147 }
1148 else {
1149 // Delete in the middle of the clip...we actually create two
1150 // NEW clips out of the left and right halves...
1151
1152 auto leftClip = std::make_unique<WaveClip>(*clip, mpFactory, true);
1153 leftClip->TrimRight(clip->GetPlayEndTime() - t0);
1154 clipsToAdd.push_back(std::move(leftClip));
1155
1156 auto rightClip = std::make_unique<WaveClip>(*clip, mpFactory, true);
1157 rightClip->TrimLeft(t1 - rightClip->GetPlayStartTime());
1158 clipsToAdd.push_back(std::move(rightClip));
1159
1160 clipsToDelete.push_back(clip.get());
1161 }
1162 }
1163 else {
1164 // (We are not doing a split cut)
1165
1166 // Don't modify this clip in place, because we want a strong
1167 // guarantee, and might modify another clip
1168 clipsToDelete.push_back( clip.get() );
1169 auto newClip = std::make_unique<WaveClip>( *clip, mpFactory, true );
1170
1171 // clip->Clear keeps points < t0 and >= t1 via Envelope::CollapseRegion
1172 newClip->Clear(t0,t1);
1173
1174 clipsToAdd.push_back( std::move( newClip ) );
1175 }
1176 }
1177 }
1178 }
1179
1180 // Only now, change the contents of this track
1181 // use No-fail-guarantee for the rest
1182
1183 if (!split && editClipCanMove)
1184 {
1185 // Clip is "behind" the region -- offset it unless we're splitting
1186 // or we're using the "don't move other clips" mode
1187 for (const auto& clip : mClips)
1188 {
1189 if (clip->BeforePlayStartTime(t1))
1190 clip->Offset(-(t1 - t0));
1191 }
1192 }
1193
1194 for (const auto &clip: clipsToDelete)
1195 {
1196 auto myIt = FindClip(mClips, clip);
1197 if (myIt != mClips.end())
1198 mClips.erase(myIt); // deletes the clip!
1199 else
1200 wxASSERT(false);
1201 }
1202
1203 for (auto &clip: clipsToAdd)
1204 mClips.push_back(std::move(clip)); // transfer ownership
1205}
1206
1207void WaveTrack::SyncLockAdjust(double oldT1, double newT1)
1208{
1209 if (newT1 > oldT1) {
1210 // Insert space within the track
1211
1212 // JKC: This is a rare case where using >= rather than > on a float matters.
1213 // GetEndTime() looks through the clips and may give us EXACTLY the same
1214 // value as T1, when T1 was set to be at the end of one of those clips.
1215 if (oldT1 >= GetEndTime())
1216 return;
1217
1218 // If track is empty at oldT1 insert whitespace; otherwise, silence
1219 if (IsEmpty(oldT1, oldT1))
1220 {
1221 // Check if clips can move
1222 if (EditClipsCanMove.Read()) {
1223 const auto offset = newT1 - oldT1;
1224 for(const auto& clip : mClips)
1225 {
1226 if (clip->GetPlayStartTime() > oldT1 - (1.0 / mRate))
1227 clip->Offset(offset);
1228 }
1229 }
1230 return;
1231 }
1232 else {
1233 // AWD: Could just use InsertSilence() on its own here, but it doesn't
1234 // follow EditClipCanMove rules (Paste() does it right)
1235 auto tmp = std::make_shared<WaveTrack>(
1237
1238 tmp->InsertSilence(0.0, newT1 - oldT1);
1239 tmp->Flush();
1240 Paste(oldT1, tmp.get());
1241 }
1242 }
1243 else if (newT1 < oldT1) {
1244 Clear(newT1, oldT1);
1245 }
1246}
1247
1248void WaveTrack::PasteWaveTrack(double t0, const WaveTrack* other)
1249{
1250 //
1251 // Pasting is a bit complicated, because with the existence of multiclip mode,
1252 // we must guess the behaviour the user wants.
1253 //
1254 // Currently, two modes are implemented:
1255 //
1256 // - If a single clip should be pasted, and it should be pasted inside another
1257 // clip, no NEW clips are generated. The audio is simply inserted.
1258 // This resembles the old (pre-multiclip support) behaviour. However, if
1259 // the clip is pasted outside of any clip, a NEW clip is generated. This is
1260 // the only behaviour which is different to what was done before, but it
1261 // shouldn't confuse users too much.
1262 //
1263 // - If multiple clips should be pasted, or a single clip that does not fill
1264 // the duration of the pasted track, these are always pasted as single
1265 // clips, and the current clip is split, when necessary. This may seem
1266 // strange at first, but it probably is better than trying to auto-merge
1267 // anything. The user can still merge the clips by hand (which should be a
1268 // simple command reachable by a hotkey or single mouse click).
1269 //
1270
1271 if (other->GetNumClips() == 0)
1272 return;
1273
1274 //wxPrintf("paste: we have at least one clip\n");
1275
1276 bool singleClipMode = other->GetNumClips() == 1 &&
1277 std::abs(other->GetStartTime()) < LongSamplesToTime(1) * 0.5;
1278
1279 const double insertDuration = other->GetEndTime();
1280 if (insertDuration != 0 && insertDuration < 1.0 / mRate)
1281 // PRL: I added this check to avoid violations of preconditions in other WaveClip and Sequence
1282 // methods, but allow the value 0 so I don't subvert the purpose of commit
1283 // 739422ba70ceb4be0bb1829b6feb0c5401de641e which causes append-recording always to make
1284 // a new clip.
1285 return;
1286
1287 //wxPrintf("Check if we need to make room for the pasted data\n");
1288
1289 auto pastingFromTempTrack = !other->GetOwner();
1290 bool editClipCanMove = GetEditClipsCanMove();
1291
1292 // Make room for the pasted data
1293 if (editClipCanMove) {
1294 if (!singleClipMode) {
1295 // We need to insert multiple clips, so split the current clip and ...
1296 SplitAt(t0);
1297 }
1298 //else if there is a clip at t0 insert new clip inside it and ...
1299
1300 // ... move everything to the right
1301 for (const auto& clip : mClips)
1302 {
1303 if (clip->GetPlayStartTime() > t0 - (1.0 / mRate))
1304 clip->Offset(insertDuration);
1305 }
1306 }
1307
1308 if (singleClipMode)
1309 {
1310 // Single clip mode
1311 // wxPrintf("paste: checking for single clip mode!\n");
1312
1313 WaveClip* insideClip = nullptr;
1314
1315 for (const auto& clip : mClips)
1316 {
1317 if (editClipCanMove)
1318 {
1319 if (clip->WithinPlayRegion(t0))
1320 {
1321 //wxPrintf("t0=%.6f: inside clip is %.6f ... %.6f\n",
1322 // t0, clip->GetStartTime(), clip->GetEndTime());
1323 insideClip = clip.get();
1324 break;
1325 }
1326 }
1327 else
1328 {
1329 // If clips are immovable we also allow prepending to clips
1330 if (clip->WithinPlayRegion(t0) ||
1331 TimeToLongSamples(t0) == clip->GetPlayStartSample())
1332 {
1333 insideClip = clip.get();
1334 break;
1335 }
1336 }
1337 }
1338
1339 if (insideClip)
1340 {
1341 // Exhibit traditional behaviour
1342 //wxPrintf("paste: traditional behaviour\n");
1343 if (!editClipCanMove)
1344 {
1345 // We did not move other clips out of the way already, so
1346 // check if we can paste without having to move other clips
1347 for (const auto& clip : mClips)
1348 {
1349 if (clip->GetPlayStartTime() > insideClip->GetPlayStartTime() &&
1350 insideClip->GetPlayEndTime() + insertDuration >
1351 clip->GetPlayStartTime())
1352 // Strong-guarantee in case of this path
1353 // not that it matters.
1356 XO("There is not enough room available to paste the selection"),
1357 XO("Warning"),
1358 "Error:_Insufficient_space_in_track"
1359 };
1360 }
1361 }
1362 insideClip->Paste(t0, other->GetClipByIndex(0));
1363 return;
1364 }
1365 // Just fall through and exhibit NEW behaviour
1366 }
1367
1368 // Insert NEW clips
1369 //wxPrintf("paste: multi clip mode!\n");
1370
1371 if (!editClipCanMove && !IsEmpty(t0, t0 + insertDuration - 1.0 / mRate))
1372 // Strong-guarantee in case of this path
1373 // not that it matters.
1376 XO("There is not enough room available to paste the selection"),
1377 XO("Warning"),
1378 "Error:_Insufficient_space_in_track"
1379 };
1380
1381 for (const auto& clip : other->mClips)
1382 {
1383 // AWD Oct. 2009: Don't actually paste in placeholder clips
1384 if (!clip->GetIsPlaceholder())
1385 {
1386 auto newClip =
1387 std::make_unique<WaveClip>(*clip, mpFactory, true);
1388 newClip->Resample(mRate);
1389 newClip->Offset(t0);
1390 newClip->MarkChanged();
1391 if (pastingFromTempTrack)
1392 //Clips from the tracks which aren't bound to any TrackList are
1393 //considered to be new entities, thus named using "new" name template
1394 newClip->SetName(MakeNewClipName());
1395 else
1396 newClip->SetName(MakeClipCopyName(clip->GetName()));
1397 mClips.push_back(std::move(newClip)); // transfer ownership
1398 }
1399 }
1400}
1401
1403void WaveTrack::Paste(double t0, const Track *src)
1404{
1405 if (auto other = dynamic_cast<const WaveTrack*>(src))
1406 PasteWaveTrack(t0, other);
1407 else
1408 // THROW_INCONSISTENCY_EXCEPTION; // ?
1409 (void)0;// Empty if intentional.
1410}
1411
1412void WaveTrack::Silence(double t0, double t1)
1413{
1414 if (t1 < t0)
1416
1417 auto start = TimeToLongSamples(t0);
1418 auto end = TimeToLongSamples(t1);
1419
1420 for (const auto &clip : mClips)
1421 {
1422 auto clipStart = clip->GetPlayStartSample();
1423 auto clipEnd = clip->GetPlayEndSample();
1424
1425 if (clipEnd > start && clipStart < end)
1426 {
1427 auto offset = std::max(start - clipStart, sampleCount(0));
1428 // Clip sample region and Get/Put sample region overlap
1429 auto length = std::min(end, clipEnd) - (clipStart + offset);
1430
1431 clip->SetSilence(offset, length);
1432 }
1433 }
1434}
1435
1437void WaveTrack::InsertSilence(double t, double len)
1438{
1439 // Nothing to do, if length is zero.
1440 // Fixes Bug 1626
1441 if( len == 0 )
1442 return;
1443 if (len <= 0)
1445
1446 if (mClips.empty())
1447 {
1448 // Special case if there is no clip yet
1449 auto clip = std::make_unique<WaveClip>(mpFactory, mFormat, mRate, this->GetWaveColorIndex());
1450 clip->InsertSilence(0, len);
1451 // use No-fail-guarantee
1452 mClips.push_back( std::move( clip ) );
1453 return;
1454 }
1455 else {
1456 // Assume at most one clip contains t
1457 const auto end = mClips.end();
1458 const auto it = std::find_if( mClips.begin(), end,
1459 [&](const WaveClipHolder &clip) { return clip->WithinPlayRegion(t); } );
1460
1461 // use Strong-guarantee
1462 if (it != end)
1463 it->get()->InsertSilence(t, len);
1464
1465 // use No-fail-guarantee
1466 for (const auto &clip : mClips)
1467 {
1468 if (clip->BeforePlayStartTime(t))
1469 clip->Offset(len);
1470 }
1471 }
1472}
1473
1474//Performs the opposite of Join
1475//Analyses selected region for possible Joined clips and disjoins them
1477void WaveTrack::Disjoin(double t0, double t1)
1478{
1480 const size_t maxAtOnce = 1048576;
1481 Floats buffer{ maxAtOnce };
1482 Regions regions;
1483
1484 for (const auto &clip : mClips)
1485 {
1486 double startTime = clip->GetPlayStartTime();
1487 double endTime = clip->GetPlayEndTime();
1488
1489 if( endTime < t0 || startTime > t1 )
1490 continue;
1491
1492 //simply look for a sequence of zeroes and if the sequence
1493 //is greater than minimum number, split-DELETE the region
1494
1495 sampleCount seqStart = -1;
1496 auto start = clip->TimeToSamples(std::max(.0, t0 - startTime));
1497 auto end = clip->TimeToSamples(std::min(endTime, t1) - startTime);
1498
1499 auto len = ( end - start );
1500 for( decltype(len) done = 0; done < len; done += maxAtOnce )
1501 {
1502 auto numSamples = limitSampleBufferSize( maxAtOnce, len - done );
1503
1504 clip->GetSamples( ( samplePtr )buffer.get(), floatSample, start + done,
1505 numSamples );
1506 for( decltype(numSamples) i = 0; i < numSamples; i++ )
1507 {
1508 auto curSamplePos = start + done + i;
1509
1510 //start a NEW sequence
1511 if( buffer[ i ] == 0.0 && seqStart == -1 )
1512 seqStart = curSamplePos;
1513 else if( buffer[ i ] != 0.0 || curSamplePos == end - 1 )
1514 {
1515 if( seqStart != -1 )
1516 {
1517 decltype(end) seqEnd;
1518
1519 //consider the end case, where selection ends in zeroes
1520 if( curSamplePos == end - 1 && buffer[ i ] == 0.0 )
1521 seqEnd = end;
1522 else
1523 seqEnd = curSamplePos;
1524 if( seqEnd - seqStart + 1 > minSamples )
1525 {
1526 regions.push_back(
1527 Region(
1528 startTime + clip->SamplesToTime(seqStart),
1529 startTime + clip->SamplesToTime(seqEnd)
1530 )
1531 );
1532 }
1533 seqStart = -1;
1534 }
1535 }
1536 }
1537 }
1538 }
1539
1540 for( unsigned int i = 0; i < regions.size(); i++ )
1541 {
1542 const Region &region = regions.at(i);
1543 SplitDelete(region.start, region.end );
1544 }
1545}
1546
1548void WaveTrack::Join(double t0, double t1)
1549{
1550 // Merge all WaveClips overlapping selection into one
1551
1552 WaveClipPointers clipsToDelete;
1553 WaveClip* newClip{};
1554
1555 for (const auto &clip: mClips)
1556 {
1557 if (clip->GetPlayStartTime() < t1-(1.0/mRate) &&
1558 clip->GetPlayEndTime()-(1.0/mRate) > t0) {
1559
1560 // Put in sorted order
1561 auto it = clipsToDelete.begin(), end = clipsToDelete.end();
1562 for (; it != end; ++it)
1563 if ((*it)->GetPlayStartTime() > clip->GetPlayStartTime())
1564 break;
1565 //wxPrintf("Insert clip %.6f at position %d\n", clip->GetStartTime(), i);
1566 clipsToDelete.insert(it, clip.get());
1567 }
1568 }
1569
1570 //if there are no clips to DELETE, nothing to do
1571 if( clipsToDelete.size() == 0 )
1572 return;
1573
1574 auto t = clipsToDelete[0]->GetPlayStartTime();
1575 //preserve left trim data if any
1576 newClip = CreateClip(clipsToDelete[0]->GetSequenceStartTime(),
1577 clipsToDelete[0]->GetName());
1578
1579 for (const auto &clip : clipsToDelete)
1580 {
1581 //wxPrintf("t=%.6f adding clip (offset %.6f, %.6f ... %.6f)\n",
1582 // t, clip->GetOffset(), clip->GetStartTime(), clip->GetEndTime());
1583
1584 if (clip->GetPlayStartTime() - t > (1.0 / mRate)) {
1585 double addedSilence = (clip->GetPlayStartTime() - t);
1586 //wxPrintf("Adding %.6f seconds of silence\n");
1587 auto offset = clip->GetPlayStartTime();
1588 auto value = clip->GetEnvelope()->GetValue( offset );
1589 newClip->AppendSilence( addedSilence, value );
1590 t += addedSilence;
1591 }
1592
1593 //wxPrintf("Pasting at %.6f\n", t);
1594 newClip->Paste(t, clip);
1595
1596 t = newClip->GetPlayEndTime();
1597
1598 auto it = FindClip(mClips, clip);
1599 mClips.erase(it); // deletes the clip
1600 }
1601}
1602
1607 size_t len, unsigned int stride, sampleFormat effectiveFormat)
1608{
1609 return RightmostOrNewClip()
1610 ->Append(buffer, format, len, stride, effectiveFormat);
1611}
1612
1614{
1615 for (const auto &clip : mClips)
1616 {
1617 const auto startSample = clip->GetPlayStartSample();
1618 const auto endSample = clip->GetPlayEndSample();
1619 if (s >= startSample && s < endSample)
1620 {
1621 auto blockStartOffset = clip->GetSequence()->GetBlockStart(clip->ToSequenceSamples(s));
1622 return std::max(startSample, clip->GetSequenceStartSample() + blockStartOffset);
1623 }
1624 }
1625
1626 return -1;
1627}
1628
1630{
1631 auto bestBlockSize = GetMaxBlockSize();
1632
1633 for (const auto &clip : mClips)
1634 {
1635 auto startSample = clip->GetPlayStartSample();
1636 auto endSample = clip->GetPlayEndSample();
1637 if (s >= startSample && s < endSample)
1638 {
1639 bestBlockSize = clip->GetSequence()->GetBestBlockSize(s - clip->GetSequenceStartSample());
1640 break;
1641 }
1642 }
1643
1644 return bestBlockSize;
1645}
1646
1648{
1649 decltype(GetMaxBlockSize()) maxblocksize = 0;
1650 for (const auto &clip : mClips)
1651 {
1652 maxblocksize = std::max(maxblocksize, clip->GetSequence()->GetMaxBlockSize());
1653 }
1654
1655 if (maxblocksize == 0)
1656 {
1657 // We really need the maximum block size, so create a
1658 // temporary sequence to get it.
1659 maxblocksize =
1661 .GetMaxBlockSize();
1662 }
1663
1664 wxASSERT(maxblocksize > 0);
1665
1666 return maxblocksize;
1667}
1668
1670{
1672}
1673
1680{
1681 // After appending, presumably. Do this to the clip that gets appended.
1683}
1684
1685bool WaveTrack::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
1686{
1687 if (tag == "wavetrack") {
1688 double dblValue;
1689 long nValue;
1690
1691 for (auto pair : attrs)
1692 {
1693 auto attr = pair.first;
1694 auto value = pair.second;
1695
1696 if (attr == "rate")
1697 {
1698 // mRate is an int, but "rate" in the project file is a float.
1699 if (!value.TryGet(dblValue) ||
1700 (dblValue < 1.0) || (dblValue > 1000000.0)) // allow a large range to be read
1701 return false;
1702
1703 mRate = lrint(dblValue);
1704 }
1705 else if (attr == "offset" && value.TryGet(dblValue))
1706 {
1707 // Offset is only relevant for legacy project files. The value
1708 // is cached until the actual WaveClip containing the legacy
1709 // track is created.
1710 mLegacyProjectFileOffset = dblValue;
1711 }
1712 else if (this->WritableSampleTrack::HandleXMLAttribute(attr, value))
1713 {}
1714 else if (this->Track::HandleCommonXMLAttribute(attr, value))
1715 ;
1716 else if (attr == "gain" && value.TryGet(dblValue))
1717 DoSetGain(dblValue);
1718 else if (attr == "pan" && value.TryGet(dblValue) &&
1719 (dblValue >= -1.0) && (dblValue <= 1.0))
1720 DoSetPan(dblValue);
1721 else if (attr == "linked" && value.TryGet(nValue))
1722 SetLinkType(ToLinkType(nValue), false);
1723 else if (attr == "colorindex" && value.TryGet(nValue))
1724 // Don't use SetWaveColorIndex as it sets the clips too.
1725 mWaveColorIndex = nValue;
1726 else if (attr == "sampleformat" && value.TryGet(nValue) &&
1728 mFormat = static_cast<sampleFormat>(nValue);
1729 } // while
1730 return true;
1731 }
1732
1733 return false;
1734}
1735
1736void WaveTrack::HandleXMLEndTag(const std::string_view& WXUNUSED(tag))
1737{
1738 // In case we opened a pre-multiclip project, we need to
1739 // simulate closing the waveclip tag.
1740 NewestOrNewClip()->HandleXMLEndTag("waveclip");
1741}
1742
1743XMLTagHandler *WaveTrack::HandleXMLChild(const std::string_view& tag)
1744{
1745 if ( auto pChild = WaveTrackIORegistry::Get()
1746 .CallObjectAccessor(tag, *this) )
1747 return pChild;
1748
1749 //
1750 // This is legacy code (1.2 and previous) and is not called for NEW projects!
1751 //
1752 if (tag == "sequence" || tag == "envelope")
1753 {
1754 // This is a legacy project, so set the cached offset
1756
1757 // Legacy project file tracks are imported as one single wave clip
1758 if (tag == "sequence")
1759 return NewestOrNewClip()->GetSequence();
1760 else if (tag == "envelope")
1761 return NewestOrNewClip()->GetEnvelope();
1762 }
1763
1764 // JKC... for 1.1.0, one step better than what we had, but still badly broken.
1765 // If we see a waveblock at this level, we'd better generate a sequence.
1766 if (tag == "waveblock")
1767 {
1768 // This is a legacy project, so set the cached offset
1771 return pSeq;
1772 }
1773
1774 //
1775 // This is for the NEW file format (post-1.2)
1776 //
1777 if (tag == "waveclip")
1778 return CreateClip();
1779
1780 return nullptr;
1781}
1782
1783void WaveTrack::WriteXML(XMLWriter &xmlFile) const
1784// may throw
1785{
1786 xmlFile.StartTag(wxT("wavetrack"));
1787 this->Track::WriteCommonXMLAttributes( xmlFile );
1788 xmlFile.WriteAttr(wxT("linked"), static_cast<int>(GetLinkType()));
1790 xmlFile.WriteAttr(wxT("rate"), mRate);
1791
1792 // Some values don't vary independently in channels but have been written
1793 // redundantly for each channel. Keep doing this in 3.4 and later in case
1794 // a project is opened in an earlier version.
1795 xmlFile.WriteAttr(wxT("gain"), static_cast<double>(GetGain()));
1796 xmlFile.WriteAttr(wxT("pan"), static_cast<double>(GetPan()));
1797 xmlFile.WriteAttr(wxT("colorindex"), mWaveColorIndex );
1798
1799 xmlFile.WriteAttr(wxT("sampleformat"), static_cast<long>(mFormat) );
1800
1801 WaveTrackIORegistry::Get().CallWriters(*this, xmlFile);
1802
1803 for (const auto &clip : mClips)
1804 {
1805 clip->WriteXML(xmlFile);
1806 }
1807
1808 xmlFile.EndTag(wxT("wavetrack"));
1809}
1810
1812{
1813 for (const auto &clip : mClips)
1814 if (clip->GetSequence()->GetErrorOpening())
1815 return true;
1816
1817 return false;
1818}
1819
1821{
1822 for (const auto &clip : mClips)
1823 clip->CloseLock();
1824
1825 return true;
1826}
1827
1829{
1830 bool found = false;
1831 double best = 0.0;
1832
1833 if (mClips.empty())
1834 return 0;
1835
1836 for (const auto &clip : mClips)
1837 if (!found)
1838 {
1839 found = true;
1840 best = clip->GetPlayStartTime();
1841 }
1842 else if (clip->GetPlayStartTime() < best)
1843 best = clip->GetPlayStartTime();
1844
1845 return best;
1846}
1847
1849{
1850 bool found = false;
1851 double best = 0.0;
1852
1853 if (mClips.empty())
1854 return 0;
1855
1856 for (const auto &clip : mClips)
1857 if (!found)
1858 {
1859 found = true;
1860 best = clip->GetPlayEndTime();
1861 }
1862 else if (clip->GetPlayEndTime() > best)
1863 best = clip->GetPlayEndTime();
1864
1865 return best;
1866}
1867
1868//
1869// Getting/setting samples. The sample counts here are
1870// expressed relative to t=0.0 at the track's sample rate.
1871//
1872
1873std::pair<float, float> WaveTrack::GetMinMax(
1874 double t0, double t1, bool mayThrow) const
1875{
1876 std::pair<float, float> results {
1877 // we need these at extremes to make sure we find true min and max
1878 FLT_MAX, -FLT_MAX
1879 };
1880 bool clipFound = false;
1881
1882 if (t0 > t1) {
1883 if (mayThrow)
1885 return results;
1886 }
1887
1888 if (t0 == t1)
1889 return results;
1890
1891 for (const auto &clip: mClips)
1892 {
1893 if (t1 >= clip->GetPlayStartTime() && t0 <= clip->GetPlayEndTime())
1894 {
1895 clipFound = true;
1896 auto clipResults = clip->GetMinMax(t0, t1, mayThrow);
1897 if (clipResults.first < results.first)
1898 results.first = clipResults.first;
1899 if (clipResults.second > results.second)
1900 results.second = clipResults.second;
1901 }
1902 }
1903
1904 if(!clipFound)
1905 {
1906 results = { 0.f, 0.f }; // sensible defaults if no clips found
1907 }
1908
1909 return results;
1910}
1911
1912float WaveTrack::GetRMS(double t0, double t1, bool mayThrow) const
1913{
1914 if (t0 > t1) {
1915 if (mayThrow)
1917 return 0.f;
1918 }
1919
1920 if (t0 == t1)
1921 return 0.f;
1922
1923 double sumsq = 0.0;
1924 sampleCount length = 0;
1925
1926 for (const auto &clip: mClips)
1927 {
1928 // If t1 == clip->GetStartTime() or t0 == clip->GetEndTime(), then the clip
1929 // is not inside the selection, so we don't want it.
1930 // if (t1 >= clip->GetStartTime() && t0 <= clip->GetEndTime())
1931 if (t1 >= clip->GetPlayStartTime() && t0 <= clip->GetPlayEndTime())
1932 {
1933 auto clipStart = clip->TimeToSequenceSamples(std::max(t0, clip->GetPlayStartTime()));
1934 auto clipEnd = clip->TimeToSequenceSamples(std::min(t1, clip->GetPlayEndTime()));
1935
1936 float cliprms = clip->GetRMS(t0, t1, mayThrow);
1937
1938 sumsq += cliprms * cliprms * (clipEnd - clipStart).as_float();
1939 length += (clipEnd - clipStart);
1940 }
1941 }
1942 return length > 0 ? sqrt(sumsq / length.as_double()) : 0.0;
1943}
1944
1946 sampleCount start, size_t len, fillFormat fill,
1947 bool mayThrow, sampleCount * pNumWithinClips) const
1948{
1949 // Simple optimization: When this buffer is completely contained within one clip,
1950 // don't clear anything (because we won't have to). Otherwise, just clear
1951 // everything to be on the safe side.
1952 bool doClear = true;
1953 bool result = true;
1954 sampleCount samplesCopied = 0;
1955 for (const auto &clip: mClips)
1956 {
1957 if (start >= clip->GetPlayStartSample() && start+len <= clip->GetPlayEndSample())
1958 {
1959 doClear = false;
1960 break;
1961 }
1962 }
1963 if (doClear)
1964 {
1965 // Usually we fill in empty space with zero
1966 if( fill == fillZero )
1967 ClearSamples(buffer, format, 0, len);
1968 // but we don't have to.
1969 else if( fill==fillTwo )
1970 {
1971 wxASSERT( format==floatSample );
1972 float * pBuffer = (float*)buffer;
1973 for(size_t i=0;i<len;i++)
1974 pBuffer[i]=2.0f;
1975 }
1976 else
1977 {
1978 wxFAIL_MSG(wxT("Invalid fill format"));
1979 }
1980 }
1981
1982 // Iterate the clips. They are not necessarily sorted by time.
1983 for (const auto &clip: mClips)
1984 {
1985 auto clipStart = clip->GetPlayStartSample();
1986 auto clipEnd = clip->GetPlayEndSample();
1987
1988 if (clipEnd > start && clipStart < start+len)
1989 {
1990 // Clip sample region and Get/Put sample region overlap
1991 auto samplesToCopy =
1992 std::min( start+len - clipStart, clip->GetPlaySamplesCount() );
1993 auto startDelta = clipStart - start;
1994 decltype(startDelta) inclipDelta = 0;
1995 if (startDelta < 0)
1996 {
1997 inclipDelta = -startDelta; // make positive value
1998 samplesToCopy -= inclipDelta;
1999 // samplesToCopy is now either len or
2000 // (clipEnd - clipStart) - (start - clipStart)
2001 // == clipEnd - start > 0
2002 // samplesToCopy is not more than len
2003 //
2004 startDelta = 0;
2005 // startDelta is zero
2006 }
2007 else {
2008 // startDelta is nonnegative and less than len
2009 // samplesToCopy is positive and not more than len
2010 }
2011
2012 if (!clip->GetSamples(
2013 (samplePtr)(((char*)buffer) +
2014 startDelta.as_size_t() *
2016 format, inclipDelta, samplesToCopy.as_size_t(), mayThrow ))
2017 result = false;
2018 else
2019 samplesCopied += samplesToCopy;
2020 }
2021 }
2022 if( pNumWithinClips )
2023 *pNumWithinClips = samplesCopied;
2024 return result;
2025}
2026
2029 sampleCount start, size_t len, sampleFormat effectiveFormat)
2030{
2031 for (const auto &clip: mClips)
2032 {
2033 auto clipStart = clip->GetPlayStartSample();
2034 auto clipEnd = clip->GetPlayEndSample();
2035
2036 if (clipEnd > start && clipStart < start+len)
2037 {
2038 // Clip sample region and Get/Put sample region overlap
2039 auto samplesToCopy =
2040 std::min( start+len - clipStart, clip->GetPlaySamplesCount() );
2041 auto startDelta = clipStart - start;
2042 decltype(startDelta) inclipDelta = 0;
2043 if (startDelta < 0)
2044 {
2045 inclipDelta = -startDelta; // make positive value
2046 samplesToCopy -= inclipDelta;
2047 // samplesToCopy is now either len or
2048 // (clipEnd - clipStart) - (start - clipStart)
2049 // == clipEnd - start > 0
2050 // samplesToCopy is not more than len
2051 //
2052 startDelta = 0;
2053 // startDelta is zero
2054 }
2055 else {
2056 // startDelta is nonnegative and less than len
2057 // samplesToCopy is positive and not more than len
2058 }
2059
2060 clip->SetSamples(
2061 buffer + startDelta.as_size_t() * SAMPLE_SIZE(format),
2062 format, inclipDelta, samplesToCopy.as_size_t(), effectiveFormat );
2063 clip->MarkChanged();
2064 }
2065 }
2066}
2067
2069{
2070 auto &clips = GetClips();
2071 return std::accumulate(clips.begin(), clips.end(), narrowestSampleFormat,
2072 [](sampleFormat format, const auto &pClip){
2073 return std::max(format,
2074 pClip->GetSequence()->GetSampleFormats().Effective());
2075 });
2076}
2077
2079{
2080 auto &clips = GetClips();
2081 return std::all_of(clips.begin(), clips.end(),
2082 [](const auto &pClip){ return pClip->GetEnvelope()->IsTrivial(); });
2083}
2084
2085void WaveTrack::GetEnvelopeValues(double *buffer, size_t bufferLen,
2086 double t0) const
2087{
2088 // The output buffer corresponds to an unbroken span of time which the callers expect
2089 // to be fully valid. As clips are processed below, the output buffer is updated with
2090 // envelope values from any portion of a clip, start, end, middle, or none at all.
2091 // Since this does not guarantee that the entire buffer is filled with values we need
2092 // to initialize the entire buffer to a default value.
2093 //
2094 // This does mean that, in the cases where a usable clip is located, the buffer value will
2095 // be set twice. Unfortunately, there is no easy way around this since the clips are not
2096 // stored in increasing time order. If they were, we could just track the time as the
2097 // buffer is filled.
2098 for (decltype(bufferLen) i = 0; i < bufferLen; i++)
2099 {
2100 buffer[i] = 1.0;
2101 }
2102
2103 double startTime = t0;
2104 auto tstep = 1.0 / mRate;
2105 double endTime = t0 + tstep * bufferLen;
2106 for (const auto &clip: mClips)
2107 {
2108 // IF clip intersects startTime..endTime THEN...
2109 auto dClipStartTime = clip->GetPlayStartTime();
2110 auto dClipEndTime = clip->GetPlayEndTime();
2111 if ((dClipStartTime < endTime) && (dClipEndTime > startTime))
2112 {
2113 auto rbuf = buffer;
2114 auto rlen = bufferLen;
2115 auto rt0 = t0;
2116
2117 if (rt0 < dClipStartTime)
2118 {
2119 // This is not more than the number of samples in
2120 // (endTime - startTime) which is bufferLen:
2121 auto nDiff = (sampleCount)floor((dClipStartTime - rt0) * mRate + 0.5);
2122 auto snDiff = nDiff.as_size_t();
2123 rbuf += snDiff;
2124 wxASSERT(snDiff <= rlen);
2125 rlen -= snDiff;
2126 rt0 = dClipStartTime;
2127 }
2128
2129 if (rt0 + rlen*tstep > dClipEndTime)
2130 {
2131 auto nClipLen = clip->GetPlayEndSample() - clip->GetPlayStartSample();
2132
2133 if (nClipLen <= 0) // Testing for bug 641, this problem is consistently '== 0', but doesn't hurt to check <.
2134 return;
2135
2136 // This check prevents problem cited in http://bugzilla.audacityteam.org/show_bug.cgi?id=528#c11,
2137 // Gale's cross_fade_out project, which was already corrupted by bug 528.
2138 // This conditional prevents the previous write past the buffer end, in clip->GetEnvelope() call.
2139 // Never increase rlen here.
2140 // PRL bug 827: rewrote it again
2141 rlen = limitSampleBufferSize( rlen, nClipLen );
2142 rlen = std::min(rlen, size_t(floor(0.5 + (dClipEndTime - rt0) / tstep)));
2143 }
2144 // Samples are obtained for the purpose of rendering a wave track,
2145 // so quantize time
2146 clip->GetEnvelope()->GetValues(rbuf, rlen, rt0, tstep);
2147 }
2148 }
2149}
2150
2151// When the time is both the end of a clip and the start of the next clip, the
2152// latter clip is returned.
2154{
2155
2156 const auto clips = SortedClipArray();
2157 auto p = std::find_if(clips.rbegin(), clips.rend(), [&] (WaveClip* const& clip) {
2158 return time >= clip->GetPlayStartTime() && time <= clip->GetPlayEndTime(); });
2159
2160 // When two clips are immediately next to each other, the GetPlayEndTime() of the first clip
2161 // and the GetPlayStartTime() of the second clip may not be exactly equal due to rounding errors.
2162 // If "time" is the end time of the first of two such clips, and the end time is slightly
2163 // less than the start time of the second clip, then the first rather than the
2164 // second clip is found by the above code. So correct this.
2165 if (p != clips.rend() && p != clips.rbegin() &&
2166 time == (*p)->GetPlayEndTime() &&
2167 (*p)->SharesBoundaryWithNextClip(*(p-1))) {
2168 p--;
2169 }
2170
2171 return p != clips.rend() ? *p : nullptr;
2172}
2173
2175{
2176 WaveClip* clip = GetClipAtTime(time);
2177 if (clip)
2178 return clip->GetEnvelope();
2179 else
2180 return NULL;
2181}
2182
2184{
2185 WaveClip* clip = GetClipAtTime(time);
2186 if (clip)
2187 return clip->GetSequence();
2188 else
2189 return NULL;
2190}
2191
2192WaveClip* WaveTrack::CreateClip(double offset, const wxString& name)
2193{
2194 auto clip = std::make_unique<WaveClip>(mpFactory, mFormat, mRate, GetWaveColorIndex());
2195 clip->SetName(name);
2196 clip->SetSequenceStartTime(offset);
2197 mClips.push_back(std::move(clip));
2198
2199 return mClips.back().get();
2200}
2201
2203{
2204 if (mClips.empty()) {
2206 }
2207 else
2208 return mClips.back().get();
2209}
2210
2213{
2214 if (mClips.empty()) {
2216 }
2217 else
2218 {
2219 auto it = mClips.begin();
2220 WaveClip *rightmost = (*it++).get();
2221 double maxOffset = rightmost->GetPlayStartTime();
2222 for (auto end = mClips.end(); it != end; ++it)
2223 {
2224 WaveClip *clip = it->get();
2225 double offset = clip->GetPlayStartTime();
2226 if (maxOffset < offset)
2227 maxOffset = offset, rightmost = clip;
2228 }
2229 return rightmost;
2230 }
2231}
2232
2233int WaveTrack::GetClipIndex(const WaveClip* clip) const
2234{
2235 int result;
2236 FindClip(mClips, clip, &result);
2237 return result;
2238}
2239
2241{
2242 if(index < (int)mClips.size())
2243 return mClips[index].get();
2244 else
2245 return nullptr;
2246}
2247
2249{
2250 return const_cast<WaveTrack&>(*this).GetClipByIndex(index);
2251}
2252
2254{
2255 return mClips.size();
2256}
2257
2259 const std::vector<WaveClip*> &clips,
2260 double amount,
2261 double *allowedAmount /* = NULL */)
2262{
2263 if (allowedAmount)
2264 *allowedAmount = amount;
2265
2266 const auto &moving = [&](WaveClip *clip){
2267 // linear search might be improved, but expecting few moving clips
2268 // compared with the fixed clips
2269 return clips.end() != std::find( clips.begin(), clips.end(), clip );
2270 };
2271
2272 for (const auto &c: mClips) {
2273 if ( moving( c.get() ) )
2274 continue;
2275 for (const auto clip : clips) {
2276 if (c->GetPlayStartTime() < clip->GetPlayEndTime() + amount &&
2277 c->GetPlayEndTime() > clip->GetPlayStartTime() + amount)
2278 {
2279 if (!allowedAmount)
2280 return false; // clips overlap
2281
2282 if (amount > 0)
2283 {
2284 if (c->GetPlayStartTime() - clip->GetPlayEndTime() < *allowedAmount)
2285 *allowedAmount = c->GetPlayStartTime() - clip->GetPlayEndTime();
2286 if (*allowedAmount < 0)
2287 *allowedAmount = 0;
2288 } else
2289 {
2290 if (c->GetPlayEndTime() - clip->GetPlayStartTime() > *allowedAmount)
2291 *allowedAmount = c->GetPlayEndTime() - clip->GetPlayStartTime();
2292 if (*allowedAmount > 0)
2293 *allowedAmount = 0;
2294 }
2295 }
2296 }
2297 }
2298
2299 if (allowedAmount)
2300 {
2301 if (*allowedAmount == amount)
2302 return true;
2303
2304 // Check if the NEW calculated amount would not violate
2305 // any other constraint
2306 if (!CanOffsetClips(clips, *allowedAmount, nullptr)) {
2307 *allowedAmount = 0; // play safe and don't allow anything
2308 return false;
2309 }
2310 else
2311 return true;
2312 } else
2313 return true;
2314}
2315
2317 WaveClip* clip, double &slideBy, double &tolerance) const
2318{
2319 for (const auto &c : mClips)
2320 {
2321 //VS: Do we need to take into account sample rate difference?
2322 double d1 = c->GetPlayStartTime() - (clip->GetPlayEndTime()+slideBy);
2323 double d2 = (clip->GetPlayStartTime()+slideBy) - c->GetPlayEndTime();
2324 if ( (d1<0) && (d2<0) )
2325 {
2326 // clips overlap.
2327 // Try to rescue it.
2328 // The rescue logic is not perfect, and will typically
2329 // move the clip at most once.
2330 // We divide by 1000 rather than set to 0, to allow for
2331 // a second 'micro move' that is really about rounding error.
2332
2333 // VS: clip could be moved more than once, moving it in opposite
2334 // direction may reintroduce overlapping condition
2335 if( -d1 < tolerance ){
2336 // right edge of clip overlaps slightly.
2337 // slide clip left a small amount.
2338 slideBy +=d1;
2339 tolerance /=1000;
2340 } else if( -d2 < tolerance ){
2341 // left edge of clip overlaps slightly.
2342 // slide clip right a small amount.
2343 slideBy -= d2;
2344 tolerance /=1000;
2345 }
2346 else
2347 return false; // clips overlap No tolerance left.
2348 }
2349 }
2350
2351 return true;
2352}
2353
2355void WaveTrack::Split( double t0, double t1 )
2356{
2357 SplitAt( t0 );
2358 if( t0 != t1 )
2359 SplitAt( t1 );
2360}
2361
2364{
2365 for (const auto &c : mClips)
2366 {
2367 if (c->WithinPlayRegion(t))
2368 {
2370 auto newClip = std::make_unique<WaveClip>( *c, mpFactory, true );
2371 c->TrimRightTo(t);// put t on a sample
2372 newClip->TrimLeftTo(t);
2373
2374 // This could invalidate the iterators for the loop! But we return
2375 // at once so it's okay
2376 mClips.push_back(std::move(newClip)); // transfer ownership
2377 return;
2378 }
2379 }
2380}
2381
2382// Expand cut line (that is, re-insert audio, then DELETE audio saved in cut line)
2384void WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
2385 double* cutlineEnd)
2386{
2387 bool editClipCanMove = GetEditClipsCanMove();
2388
2389 // Find clip which contains this cut line
2390 double start = 0, end = 0;
2391 auto pEnd = mClips.end();
2392 auto pClip = std::find_if( mClips.begin(), pEnd,
2393 [&](const WaveClipHolder &clip) {
2394 return clip->FindCutLine(cutLinePosition, &start, &end); } );
2395 if (pClip != pEnd)
2396 {
2397 auto &clip = *pClip;
2398 if (!editClipCanMove)
2399 {
2400 // We are not allowed to move the other clips, so see if there
2401 // is enough room to expand the cut line
2402 for (const auto &clip2: mClips)
2403 {
2404 if (clip2->GetPlayStartTime() > clip->GetPlayStartTime() &&
2405 clip->GetPlayEndTime() + end - start > clip2->GetPlayStartTime())
2406 // Strong-guarantee in case of this path
2409 XO("There is not enough room available to expand the cut line"),
2410 XO("Warning"),
2411 "Error:_Insufficient_space_in_track"
2412 };
2413 }
2414 }
2415
2416 clip->ExpandCutLine(cutLinePosition);
2417
2418 // Strong-guarantee provided that the following gives No-fail-guarantee
2419
2420 if (cutlineStart)
2421 *cutlineStart = start;
2422 if (cutlineEnd)
2423 *cutlineEnd = end;
2424
2425 // Move clips which are to the right of the cut line
2426 if (editClipCanMove)
2427 {
2428 for (const auto &clip2 : mClips)
2429 {
2430 if (clip2->GetPlayStartTime() > clip->GetPlayStartTime())
2431 clip2->Offset(end - start);
2432 }
2433 }
2434 }
2435}
2436
2437bool WaveTrack::RemoveCutLine(double cutLinePosition)
2438{
2439 for (const auto &clip : mClips)
2440 if (clip->RemoveCutLine(cutLinePosition))
2441 return true;
2442
2443 return false;
2444}
2445
2447void WaveTrack::MergeClips(int clipidx1, int clipidx2)
2448{
2449 WaveClip* clip1 = GetClipByIndex(clipidx1);
2450 WaveClip* clip2 = GetClipByIndex(clipidx2);
2451
2452 if (!clip1 || !clip2) // Could happen if one track of a linked pair had a split and the other didn't.
2453 return; // Don't throw, just do nothing.
2454
2455 // Append data from second clip to first clip
2456 // use Strong-guarantee
2457 clip1->Paste(clip1->GetPlayEndTime(), clip2);
2458
2459 // use No-fail-guarantee for the rest
2460 // Delete second clip
2461 auto it = FindClip(mClips, clip2);
2462 mClips.erase(it);
2463}
2464
2468{
2469 for (const auto &clip : mClips)
2470 clip->Resample(rate, progress);
2471
2472 mRate = rate;
2473}
2474
2475namespace {
2476 template < typename Cont1, typename Cont2 >
2477 Cont1 FillSortedClipArray(const Cont2& mClips)
2478 {
2479 Cont1 clips;
2480 for (const auto &clip : mClips)
2481 clips.push_back(clip.get());
2482 std::sort(clips.begin(), clips.end(),
2483 [](const WaveClip *a, const WaveClip *b)
2484 { return a->GetPlayStartTime() < b->GetPlayStartTime(); });
2485 return clips;
2486 }
2487}
2488
2490{
2491 return FillSortedClipArray<WaveClipPointers>(mClips);
2492}
2493
2495{
2496 return FillSortedClipArray<WaveClipConstPointers>(mClips);
2497}
2498
2500{
2501 // The unspecified sequence is a post-order, but there is no
2502 // promise whether sister nodes are ordered in time.
2503 if ( !mStack.empty() ) {
2504 auto &pair = mStack.back();
2505 if ( ++pair.first == pair.second ) {
2506 mStack.pop_back();
2507 }
2508 else
2509 push( (*pair.first)->GetCutLines() );
2510 }
2511
2512 return *this;
2513}
2514
2516{
2517 auto pClips = &clips;
2518 while (!pClips->empty()) {
2519 auto first = pClips->begin();
2520 mStack.push_back( Pair( first, pClips->end() ) );
2521 pClips = &(*first)->GetCutLines();
2522 }
2523}
2524
2525#include "SampleBlock.h"
2526void VisitBlocks(TrackList &tracks, BlockVisitor visitor,
2527 SampleBlockIDSet *pIDs)
2528{
2529 for (auto wt : tracks.Any< const WaveTrack >()) {
2530 // Scan all clips within current track
2531 for(const auto &clip : wt->GetAllClips()) {
2532 // Scan all sample blocks within current clip
2533 auto blocks = clip->GetSequenceBlockArray();
2534 for (const auto &block : *blocks) {
2535 auto &pBlock = block.sb;
2536 if ( pBlock ) {
2537 if ( pIDs && !pIDs->insert(pBlock->GetBlockID()).second )
2538 continue;
2539 if ( visitor )
2540 visitor( *pBlock );
2541 }
2542 }
2543 }
2544 }
2545}
2546
2547void InspectBlocks(const TrackList &tracks, BlockInspector inspector,
2548 SampleBlockIDSet *pIDs)
2549{
2551 const_cast<TrackList &>(tracks), std::move( inspector ), pIDs );
2552}
2553
2554#include "Project.h"
2555#include "SampleBlock.h"
2556static auto TrackFactoryFactory = []( AudacityProject &project ) {
2557 return std::make_shared< WaveTrackFactory >(
2558 ProjectRate::Get( project ),
2559 SampleBlockFactory::New( project ) );
2560};
2561
2564};
2565
2567{
2568 return project.AttachedObjects::Get< WaveTrackFactory >( key2 );
2569}
2570
2572{
2573 return Get( const_cast< AudacityProject & >( project ) );
2574}
2575
2577{
2578 auto result = TrackFactoryFactory( project );
2579 project.AttachedObjects::Assign( key2, result );
2580 return *result;
2581}
2582
2584{
2585 project.AttachedObjects::Assign( key2, nullptr );
2586}
2587
2589 [](const AudacityProject& project) -> ProjectFormatVersion
2590 {
2591 const TrackList& trackList = TrackList::Get(project);
2592
2593 for (auto wt : trackList.Any<const WaveTrack>())
2594 {
2595 for (const auto& clip : wt->GetAllClips())
2596 {
2597 if (clip->GetTrimLeft() > 0.0 || clip->GetTrimRight() > 0.0)
2598 return { 3, 1, 0, 0 };
2599 }
2600 }
2601
2603 }
2604);
2605
2607 L"/GUI/TrackNames/DefaultTrackName",
2608 // Computed default value depends on chosen language
2609 []{ return DefaultName.Translation(); }
2610};
2611
2612// Bug 825 is essentially that SyncLock requires EditClipsCanMove.
2613// SyncLock needs rethinking, but meanwhile this function
2614// fixes the issues of Bug 825 by allowing clips to move when in
2615// SyncLock.
2617{
2618 bool mIsSyncLocked = SyncLockTracks.Read();
2619 if( mIsSyncLocked )
2620 return true;
2621 bool editClipsCanMove;
2622 return EditClipsCanMove.Read();
2623}
2624
2626 L"/GUI/EditClipCanMove", false };
2627
wxT("CloseDown"))
@ BadUserAction
Indicates that the user performed an action that is not allowed.
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:76
int format
Definition: ExportPCM.cpp:53
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
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:29
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
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:55
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:50
const char * constSamplePtr
Definition: SampleFormat.h:56
fillFormat
Definition: SampleFormat.h:59
@ fillZero
Definition: SampleFormat.h:60
@ fillTwo
Definition: SampleFormat.h:61
BoolSetting SyncLockTracks
Definition: SyncLock.cpp:172
Contains declarations for TimeWarper, IdentityTimeWarper, ShiftTimeWarper, LinearTimeWarper,...
WaveTrack::Region Region
std::shared_ptr< WaveClip > WaveClipHolder
Definition: WaveClip.h:41
std::vector< WaveClipHolder > WaveClipHolders
Definition: WaveClip.h:42
ProjectFormatExtensionsRegistry::Extension smartClipsExtension([](const AudacityProject &project) -> ProjectFormatVersion { const TrackList &trackList=TrackList::Get(project);for(auto wt :trackList.Any< const WaveTrack >()) { for(const auto &clip :wt->GetAllClips()) { if(clip->GetTrimLeft() > 0.0||clip->GetTrimRight() > 0.0) return { 3, 1, 0, 0 };} } return BaseProjectFormatVersion;})
bool GetEditClipsCanMove()
Definition: WaveTrack.cpp:2616
static ProjectFileIORegistry::ObjectReaderEntry readerEntry
Definition: WaveTrack.cpp:177
static const AudacityProject::AttachedObjects::RegisteredFactory key2
Definition: WaveTrack.cpp:2562
DEFINE_XML_METHOD_REGISTRY(WaveTrackIORegistry)
BoolSetting EditClipsCanMove
Definition: WaveTrack.cpp:2625
static auto DefaultName
Definition: WaveTrack.cpp:162
static Container MakeIntervals(const std::vector< WaveClipHolder > &clips)
Definition: WaveTrack.cpp:326
static auto TrackFactoryFactory
Definition: WaveTrack.cpp:2556
void VisitBlocks(TrackList &tracks, BlockVisitor visitor, SampleBlockIDSet *pIDs)
Definition: WaveTrack.cpp:2526
StringSetting AudioTrackNameSetting
Definition: WaveTrack.cpp:2606
void InspectBlocks(const TrackList &tracks, BlockInspector inspector, SampleBlockIDSet *pIDs)
Definition: WaveTrack.cpp:2547
static const Track::TypeInfo & typeInfo()
Definition: WaveTrack.cpp:307
std::function< void(SampleBlock &) > BlockVisitor
Definition: WaveTrack.h:540
#define WAVETRACK_MERGE_POINT_TOLERANCE
Definition: WaveTrack.h:46
std::unordered_set< SampleBlockID > SampleBlockIDSet
Definition: WaveTrack.h:538
std::function< void(const SampleBlock &) > BlockInspector
Definition: WaveTrack.h:541
std::vector< WaveClip * > WaveClipPointers
Definition: WaveTrack.h:40
std::vector< const WaveClip * > WaveClipConstPointers
Definition: WaveTrack.h:41
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:156
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Definition: Prefs.h:339
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
Utility to register hooks into a host class that attach client data.
Definition: ClientData.h:220
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:333
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:72
No change to time at all.
Definition: TimeWarper.h:69
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
void WriteXMLAttributes(XMLWriter &xmlFile) const
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
static SampleBlockFactoryPtr New(AudacityProject &project)
Definition: SampleBlock.cpp:16
Two sample formats, remembering format of original source and describing stored format.
Definition: SampleFormat.h:77
double LongSamplesToTime(sampleCount pos) const
Convert correctly between a number of samples and an (absolute) time in seconds.
Definition: SampleTrack.cpp:59
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: SampleTrack.cpp:54
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
Definition: Sequence.h:52
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:1855
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:206
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
A MessageBoxException that shows a given, unvarying string.
Specialization of Setting for strings.
Definition: Prefs.h:363
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:161
void Notify(bool allChannels, int code=-1)
Definition: Track.cpp:265
double mOffset
Definition: Track.h:372
virtual bool LinkConsistencyFix(bool doFix=true, bool completeList=true)
Check consistency of channel groups, and maybe fix it.
Definition: Track.cpp:313
ChannelGroupData & GetGroupData()
Definition: Track.cpp:171
std::shared_ptr< TrackList > GetOwner() const
Definition: Track.h:333
R TypeSwitch(const Functions &...functions)
Use this function rather than testing track type explicitly and making down-casts.
Definition: Track.h:755
void SetLinkType(LinkType linkType, bool completeList=true)
Definition: Track.cpp:144
std::shared_ptr< Track > Holder
Definition: Track.h:295
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
Definition: Track.cpp:1165
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:64
void Init(const Track &orig)
Definition: Track.cpp:55
void WriteCommonXMLAttributes(XMLWriter &xmlFile, bool includeNameAndSelected=true) const
Definition: Track.cpp:1149
virtual double GetEndTime() const =0
std::vector< Interval > Intervals
Definition: Track.h:262
LinkType
For two tracks describes the type of the linkage.
Definition: Track.h:172
LinkType GetLinkType() const noexcept
Definition: Track.cpp:1212
std::vector< ConstInterval > ConstIntervals
Definition: Track.h:264
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1201
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1302
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:360
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:101
double GetSequenceStartTime() const noexcept
Definition: WaveClip.cpp:967
void SetSequenceStartTime(double startTime)
Definition: WaveClip.cpp:973
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride, sampleFormat effectiveFormat)
Definition: WaveClip.cpp:242
double GetPlayStartTime() const noexcept
Definition: WaveClip.cpp:889
double GetTrimRight() const noexcept
Returns the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:942
double GetTrimLeft() const noexcept
Returns the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:932
Sequence * GetSequence()
Definition: WaveClip.h:227
void Paste(double t0, const WaveClip *other)
Paste data from other clip, resampling it if not equal rate.
Definition: WaveClip.cpp:374
void HandleXMLEndTag(const std::string_view &tag) override
Definition: WaveClip.cpp:326
double GetPlayEndTime() const
Definition: WaveClip.cpp:899
void SetTrimRight(double trim)
Sets the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:937
Envelope * GetEnvelope()
Definition: WaveClip.h:219
void Offset(double delta) noexcept
Definition: WaveClip.cpp:1000
void SetTrimLeft(double trim)
Sets the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:927
void Flush()
Flush must be called after last Append.
Definition: WaveClip.cpp:260
std::pair< Iterator, Iterator > Pair
Definition: WaveTrack.h:362
void push(WaveClipHolders &clips)
Definition: WaveTrack.cpp:2515
AllClipsIterator & operator++()
Definition: WaveTrack.cpp:2499
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:558
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
Definition: WaveTrack.cpp:182
static void Destroy(AudacityProject &project)
Definition: WaveTrack.cpp:2583
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:2566
static WaveTrackFactory & Reset(AudacityProject &project)
Definition: WaveTrack.cpp:2576
SampleBlockFactoryPtr mpFactory
Definition: WaveTrack.h:594
const ProjectRate & mRate
Definition: WaveTrack.h:593
A Track that contains audio waveform data.
Definition: WaveTrack.h:51
void HandleXMLEndTag(const std::string_view &tag) override
Definition: WaveTrack.cpp:1736
bool LinkConsistencyFix(bool doFix, bool completeList) override
Check consistency of channel groups, and maybe fix it.
Definition: WaveTrack.cpp:274
Track::Holder PasteInto(AudacityProject &) const override
Find or create the destination track for a paste, maybe in a different project.
Definition: WaveTrack.cpp:336
SampleBlockFactoryPtr mpFactory
Definition: WaveTrack.h:526
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1, sampleFormat effectiveFormat=widestSampleFormat) override
Definition: WaveTrack.cpp:1606
void SetRate(double newRate)
Definition: WaveTrack.cpp:401
void Paste(double t0, const Track *src) override
Definition: WaveTrack.cpp:1403
void DoSetPan(float value)
Definition: WaveTrack.cpp:436
bool CanInsertClip(WaveClip *clip, double &slideBy, double &tolerance) const
Definition: WaveTrack.cpp:2316
static WaveTrack * New(AudacityProject &project)
Definition: WaveTrack.cpp:192
void SplitDelete(double t0, double t1)
Definition: WaveTrack.cpp:1006
void SplitAt(double t)
Definition: WaveTrack.cpp:2363
std::vector< Region > Regions
Definition: WaveTrack.h:69
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const override
Definition: WaveTrack.cpp:1945
bool AddClip(const std::shared_ptr< WaveClip > &clip)
Append a clip to the track; which must have the same block factory as this track; return success.
Definition: WaveTrack.cpp:1061
void Reinit(const WaveTrack &orig)
Definition: WaveTrack.cpp:234
ConstIntervals GetIntervals() const override
Report times on the track where important intervals begin and end, for UI to snap to.
Definition: WaveTrack.cpp:345
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:2258
void ExpandCutLine(double cutLinePosition, double *cutlineStart=NULL, double *cutlineEnd=NULL)
Definition: WaveTrack.cpp:2384
double GetStartTime() const override
Get the time at which the first clip in the track starts.
Definition: WaveTrack.cpp:1828
void ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
Definition: WaveTrack.cpp:501
size_t GetMaxBlockSize() const override
This returns a nonnegative number of samples meant to size a memory buffer.
Definition: WaveTrack.cpp:1647
sampleFormat mFormat
Definition: WaveTrack.h:516
void InsertSilence(double t, double len) override
Definition: WaveTrack.cpp:1437
size_t GetBestBlockSize(sampleCount t) const override
This returns a nonnegative number of samples meant to size a memory buffer.
Definition: WaveTrack.cpp:1629
WaveClipPointers SortedClipArray()
Definition: WaveTrack.cpp:2489
void WriteXML(XMLWriter &xmlFile) const override
Definition: WaveTrack.cpp:1783
wxString MakeNewClipName() const
Definition: WaveTrack.cpp:384
WaveClip * CreateClip(double offset=.0, const wxString &name=wxEmptyString)
Definition: WaveTrack.cpp:2192
static wxString GetDefaultAudioTrackNamePreference()
Definition: WaveTrack.cpp:164
sampleCount GetBlockStart(sampleCount t) const override
This returns a possibly large or negative value.
Definition: WaveTrack.cpp:1613
void Join(double t0, double t1)
Definition: WaveTrack.cpp:1548
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
Definition: WaveTrack.cpp:1873
int GetWaveColorIndex() const
Definition: WaveTrack.h:142
WaveClip * NewestOrNewClip()
Get access to the most recently added clip, or create a clip, if there is not already one....
Definition: WaveTrack.cpp:2202
sampleFormat GetSampleFormat() const override
Definition: WaveTrack.h:148
void Silence(double t0, double t1) override
Definition: WaveTrack.cpp:1412
void ClearAndAddCutLine(double t0, double t1)
Definition: WaveTrack.cpp:694
void SyncLockAdjust(double oldT1, double newT1) override
Definition: WaveTrack.cpp:1207
const TypeInfo & GetTypeInfo() const override
Definition: WaveTrack.cpp:315
WaveClip * GetClipByIndex(int index)
Definition: WaveTrack.cpp:2240
WaveTrack(const SampleBlockFactoryPtr &pFactory, sampleFormat format, double rate)
Definition: WaveTrack.cpp:201
float GetPan() const
Definition: WaveTrack.cpp:431
size_t GetIdealBlockSize()
Definition: WaveTrack.cpp:1669
WaveClip * GetClipAtTime(double time)
Definition: WaveTrack.cpp:2153
Track::Holder Cut(double t0, double t1) override
Definition: WaveTrack.cpp:532
bool GetErrorOpening() override
Definition: WaveTrack.cpp:1811
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:688
void Init(const WaveTrack &orig)
Definition: WaveTrack.cpp:224
void Split(double t0, double t1)
Definition: WaveTrack.cpp:2355
void Flush() override
Definition: WaveTrack.cpp:1679
bool RemoveCutLine(double cutLinePosition)
Definition: WaveTrack.cpp:2437
sampleCount GetPlaySamplesCount() const
Definition: WaveTrack.cpp:480
void SetWaveColorIndex(int colorIndex)
Definition: WaveTrack.cpp:473
sampleCount GetSequenceSamplesCount() const
Definition: WaveTrack.cpp:490
Track::Holder Clone() const override
Definition: WaveTrack.cpp:365
double GetOffset() const override
Definition: WaveTrack.cpp:257
bool IsEmpty(double t0, double t1) const
Returns true if there are no WaveClips in the specified region.
Definition: WaveTrack.cpp:510
sampleFormat WidestEffectiveFormat() const override
Definition: WaveTrack.cpp:2068
void SetPan(float newPan)
Definition: WaveTrack.cpp:441
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
Definition: WaveTrack.cpp:1743
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
Definition: WaveTrack.cpp:1685
static const TypeInfo & ClassTypeInfo()
Definition: WaveTrack.cpp:320
std::shared_ptr< WaveClip > RemoveAndReturnClip(WaveClip *clip)
Definition: WaveTrack.cpp:1048
void Trim(double t0, double t1)
Definition: WaveTrack.cpp:578
void Disjoin(double t0, double t1)
Definition: WaveTrack.cpp:1477
void DoSetGain(float value)
Definition: WaveTrack.cpp:418
void Resample(int rate, BasicUI::ProgressDialog *progress=NULL)
Definition: WaveTrack.cpp:2467
float GetRMS(double t0, double t1, bool mayThrow=true) const
Definition: WaveTrack.cpp:1912
double GetEndTime() const override
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1848
float GetGain() const
Definition: WaveTrack.cpp:413
const WaveClip * FindClipByName(const wxString &name) const
Returns nullptr if clip with such name was not found.
Definition: WaveTrack.cpp:355
int GetClipIndex(const WaveClip *clip) const
Definition: WaveTrack.cpp:2233
void SetGain(float newGain)
Definition: WaveTrack.cpp:423
double GetRate() const override
Definition: WaveTrack.cpp:396
WaveClip * RightmostOrNewClip()
Get access to the last (rightmost) clip, or create a clip, if there is not already one.
Definition: WaveTrack.cpp:2212
Sequence * GetSequenceAtTime(double time)
Definition: WaveTrack.cpp:2183
Track::Holder CopyNonconst(double t0, double t1)
Definition: WaveTrack.cpp:682
WaveClipHolders & GetClips()
Definition: WaveTrack.h:319
int GetNumClips() const
Definition: WaveTrack.cpp:2253
float GetChannelGain(int channel) const override
Takes gain and pan into account.
Definition: WaveTrack.cpp:454
void Merge(const Track &orig)
Definition: WaveTrack.cpp:243
void Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Definition: WaveTrack.cpp:2028
bool HasTrivialEnvelope() const override
Definition: WaveTrack.cpp:2078
void MergeClips(int clipidx1, int clipidx2)
Definition: WaveTrack.cpp:2447
int mWaveColorIndex
Definition: WaveTrack.h:518
double mLegacyProjectFileOffset
Definition: WaveTrack.h:530
int mRate
Definition: WaveTrack.h:517
void ClearAndPaste(double t0, double t1, const Track *src, bool preserve=true, bool merge=true, const TimeWarper *effectWarper=NULL)
Definition: WaveTrack.cpp:746
std::shared_ptr< WaveTrack > Holder
Definition: WaveTrack.h:99
void HandleClear(double t0, double t1, bool addCutLines, bool split)
Definition: WaveTrack.cpp:1074
void SetOffset(double o) override
Definition: WaveTrack.cpp:263
Track::Holder SplitCut(double t0, double t1)
Definition: WaveTrack.cpp:545
WaveClipHolders mClips
Definition: WaveTrack.h:514
Track::Holder Copy(double t0, double t1, bool forClipboard=true) const override
Definition: WaveTrack.cpp:621
Envelope * GetEnvelopeAtTime(double time)
Definition: WaveTrack.cpp:2174
Holder EmptyCopy(const SampleBlockFactoryPtr &pFactory={}, bool keepLink=true) const
Definition: WaveTrack.cpp:610
void PasteWaveTrack(double t0, const WaveTrack *other)
Definition: WaveTrack.cpp:1248
virtual ~WaveTrack()
Definition: WaveTrack.cpp:253
bool CloseLock()
Definition: WaveTrack.cpp:1820
void GetEnvelopeValues(double *buffer, size_t bufferLen, double t0) const override
Fetch envelope values corresponding to uniformly separated sample times starting at the given time.
Definition: WaveTrack.cpp:2085
wxString MakeClipCopyName(const wxString &originalName) const
Definition: WaveTrack.cpp:372
static const TypeInfo & ClassTypeInfo()
Definition: SampleTrack.cpp:82
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
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
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:196
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
PROJECT_RATE_API sampleFormat SampleFormatChoice()
static const Track::ChannelGroupAttachments::RegisteredFactory gainAndPanFactory
Definition: WaveTrack.cpp:88
WaveClipHolders::iterator FindClip(WaveClipHolders &list, const WaveClip *clip, int *distance=nullptr)
Definition: WaveTrack.cpp:1032
bool AreAligned(const WaveClipPointers &a, const WaveClipPointers &b)
Definition: WaveTrack.cpp:132
Track::LinkType ToLinkType(int value)
Definition: WaveTrack.cpp:151
Cont1 FillSortedClipArray(const Cont2 &mClips)
Definition: WaveTrack.cpp:2477
__finl float_x4 __vecc sqrt(const float_x4 &a)
STL namespace.
A convenient base class defining abstract virtual Clone() for a given kind of pointer.
Definition: ClientData.h:48
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:168
Structure to hold region of a wavetrack and a comparison function for sortability.
Definition: WaveTrack.h:56
GainAndPan & operator=(const GainAndPan &)=delete