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