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