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