Audacity 3.2.0
WaveClip.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 WaveClip.cpp
6
7 ?? Dominic Mazzoni
8 ?? Markus Meyer
9
10*******************************************************************//*******************************************************************/
16#include "WaveClip.h"
17
18#include <math.h>
19#include <numeric>
20#include <optional>
21#include <vector>
22#include <wx/log.h>
23
24#include "BasicUI.h"
25#include "Envelope.h"
27#include "Resample.h"
28#include "Sequence.h"
30#include "UserException.h"
31
32#ifdef _OPENMP
33#include <omp.h>
34#endif
35
36const char *WaveClip::WaveClip_tag = "waveclip";
37
39
41{
42}
43
45 const std::string_view &, const XMLAttributeValueView &)
46{
47 return false;
48}
49
51{
52}
53
55{
56}
57
59{
60}
61
63
65{
66 return GetClip().GetEnvelope();
67}
68
70{
71 return GetClip().GetEnvelope();
72}
73
74bool WaveClipChannel::Intersects(double t0, double t1) const
75{
76 return GetClip().IntersectsPlayRegion(t0, t1);
77}
78
80{
81 return GetClip().GetPlayStartTime();
82}
83
85{
86 return GetClip().GetPlayEndTime();
87}
88
90WaveClipChannel::GetSampleView(double t0, double t1, bool mayThrow) const
91{
92 return GetClip().GetSampleView(miChannel, t0, t1, mayThrow);
93}
94
96{
97 return GetClip().WithinPlayRegion(t);
98}
99
101{
102 return GetClip().SamplesToTime(s);
103}
104
106{
107 return GetClip().HasPitchOrSpeed();
108}
109
111{
112 return GetClip().GetTrimLeft();
113}
114
116 sampleCount start, size_t len, bool mayThrow) const
117{
118 return GetClip().GetSamples(miChannel, buffer, format, start, len, mayThrow);
119}
120
122 sampleCount start, size_t length, bool mayThrow) const
123{
124 return GetClip().GetSampleView(miChannel, start, length, mayThrow);
125}
126
128{
129 const auto pSequence = GetClip().GetSequence(miChannel);
130 // Assume sufficiently wide clip
131 assert(pSequence);
132 return *pSequence;
133}
134
136{
138}
139
141{
143}
144
146{
148}
149
150std::pair<float, float>
151WaveClipChannel::GetMinMax(double t0, double t1, bool mayThrow) const
152{
153 return GetClip().GetMinMax(miChannel, t0, t1, mayThrow);
154}
155
156float WaveClipChannel::GetRMS(double t0, double t1, bool mayThrow) const
157{
158 return GetClip().GetRMS(miChannel, t0, t1, mayThrow);
159}
160
162{
163 return GetClip().GetPlayStartSample();
164}
165
167{
168 return GetClip().GetPlayEndSample();
169}
170
172 sampleCount start, size_t len, sampleFormat effectiveFormat)
173{
175 buffer, format, start, len, effectiveFormat);
176}
177
179{
180 GetClip().WriteXML(miChannel, xmlFile);
181}
182
184{
185 return GetClip().GetTrimRight();
186}
187
189{
191}
192
194{
195 return GetClip().GetRate();
196}
197
199{
200 return GetClip().GetPlayStartTime();
201}
202
204{
205 return GetClip().GetPlayEndTime();
206}
207
209{
210 return GetPlayEndTime() - GetPlayStartTime();
211}
212
214{
215 return GetClip().TimeToSamples(time);
216}
217
219{
220 return GetClip().GetStretchRatio();
221}
222
225 sampleFormat format, int rate)
226{
227 assert(width > 0);
228 mRate = rate;
229 mSequences.resize(width);
230 for (auto &pSequence : mSequences)
231 pSequence = std::make_unique<Sequence>(factory,
233
234 mEnvelope = std::make_unique<Envelope>(true, 1e-7, 2.0, 1.0);
235 assert(CheckInvariants());
236}
237
239 const WaveClip& orig, const SampleBlockFactoryPtr& factory,
240 bool copyCutlines, CreateToken token)
241 : mCentShift { orig.mCentShift }
242 , mPitchAndSpeedPreset { orig.mPitchAndSpeedPreset }
243 , mClipStretchRatio { orig.mClipStretchRatio }
244 , mRawAudioTempo { orig.mRawAudioTempo }
245 , mProjectTempo { orig.mProjectTempo }
246{
247 // essentially a copy constructor - but you must pass in the
248 // current sample block factory, because we might be copying
249 // from one project to another
250
252 mTrimLeft = orig.mTrimLeft;
253 mTrimRight = orig.mTrimRight;
254 mRate = orig.mRate;
255
256 // Deep copy of attachments
257 Attachments &attachments = *this;
258 attachments = orig;
259
260 mSequences.reserve(orig.NChannels());
261 if (!token.emptyCopy)
262 for (auto &pSequence : orig.mSequences)
263 mSequences.push_back(std::make_unique<Sequence>(*pSequence, factory));
264
265 mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
266
267 mName = orig.mName;
268
269 if (copyCutlines)
270 for (const auto &clip: orig.mCutLines)
271 mCutLines.push_back(
272 std::make_shared<WaveClip>(*clip, factory, true, token));
273
275
276 assert(NChannels() == (token.emptyCopy ? 0 : orig.NChannels()));
277 assert(token.emptyCopy || CheckInvariants());
278 assert(!copyCutlines || NumCutLines() == orig.NumCutLines());
279}
280
282 const WaveClip& orig, const SampleBlockFactoryPtr& factory,
283 bool copyCutlines, double t0, double t1)
284 : mCentShift { orig.mCentShift }
285 , mClipStretchRatio { orig.mClipStretchRatio }
286 , mRawAudioTempo { orig.mRawAudioTempo }
287 , mProjectTempo { orig.mProjectTempo }
288{
289 assert(orig.CountSamples(t0, t1) > 0);
290
292
293 //Adjust trim values to sample-boundary
294 if(t0 > orig.GetPlayStartTime()) {
295 const auto s0 = orig.TimeToSamples(t0 - orig.GetSequenceStartTime());
296 mTrimLeft = orig.SamplesToTime(s0);
297
298 }
299 else
300 mTrimLeft = orig.mTrimLeft;
301
302 if(t1 < orig.GetPlayEndTime())
303 {
304 const auto s1 = orig.TimeToSamples(orig.GetSequenceEndTime() - t1);
305 mTrimRight = orig.SamplesToTime(s1);
306 }
307 else
308 mTrimRight = orig.mTrimRight;
309
310 mRate = orig.mRate;
311
312 // Deep copy of attachments
313 Attachments &attachments = *this;
314 attachments = orig;
315
317
318 mSequences.reserve(orig.NChannels());
319 for (auto &pSequence : orig.mSequences)
320 mSequences.push_back(
321 std::make_unique<Sequence>(*pSequence, factory));
322
323 mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
324
325 if (copyCutlines)
326 for (const auto &cutline : orig.mCutLines)
327 mCutLines.push_back(
328 std::make_shared<WaveClip>(*cutline, factory, true));
329
330 assert(NChannels() == orig.NChannels());
331 assert(CheckInvariants());
332}
333
334
336{
338}
339
340double WaveClip::Start() const
341{
342 return GetPlayStartTime();
343}
344
345double WaveClip::End() const
346{
347 return GetPlayEndTime();
348}
349
350std::shared_ptr<ChannelInterval> WaveClip::DoGetChannel(size_t iChannel)
351{
352 return std::make_shared<Channel>(*this, iChannel);
353}
354
356 size_t ii, sampleCount start, size_t length, bool mayThrow) const
357{
358 assert(ii < NChannels());
359 return mSequences[ii]->GetFloatSampleView(
360 start + TimeToSamples(mTrimLeft), length, mayThrow);
361}
362
364 size_t iChannel, double t0, double t1, bool mayThrow) const
365{
366 assert(iChannel < NChannels());
367 const auto start = TimeToSamples(std::max(0., t0));
368 const auto length =
369 (std::min(GetNumSamples(), TimeToSamples(t1)) - start).as_size_t();
370 return GetSampleView(iChannel, start, length, mayThrow);
371}
372
374{
375 return mSequences.size();
376}
377
378bool WaveClip::GetSamples(size_t ii,
380 sampleCount start, size_t len, bool mayThrow) const
381{
382 assert(ii < NChannels());
383 return mSequences[ii]
384 ->Get(buffer, format, start + TimeToSamples(mTrimLeft), len, mayThrow);
385}
386
388 sampleCount start, size_t len, bool mayThrow) const
389{
390 bool result = true;
391 for (size_t ii = 0, width = NChannels(); result && ii < width; ++ii)
392 result = GetSamples(ii, buffers[ii], format, start, len, mayThrow);
393 return result;
394}
395
397void WaveClip::SetSamples(size_t ii,
399 sampleCount start, size_t len, sampleFormat effectiveFormat)
400{
402 assert(ii < NChannels());
403 // use Strong-guarantee
404 mSequences[ii]->SetSamples(buffer, format,
405 start + TimeToSamples(mTrimLeft), len, effectiveFormat);
406
407 // use No-fail-guarantee
408 MarkChanged();
409}
410
411void WaveClip::SetEnvelope(std::unique_ptr<Envelope> p)
412{
413 assert(p);
414 mEnvelope = move(p);
415}
416
418{
419 assert(ii < NChannels());
420 return &mSequences[ii]->GetBlockArray();
421}
422
424{
425 assert(iChannel < NChannels());
426 return mSequences[iChannel]->GetAppendBufferLen();
427}
428
430{
431 mSequences.resize(1);
432 this->Attachments::ForEach([](WaveClipListener &attachment){
433 attachment.Erase(1);
434 });
435 for (auto &pCutline : mCutLines)
436 pCutline->DiscardRightChannel();
437 assert(NChannels() == 1);
438 assert(CheckInvariants());
439}
440
442{
443 assert(NChannels() == 2);
444 this->Attachments::ForEach([](WaveClipListener &attachment){
445 attachment.SwapChannels();
446 });
448 for (auto &pCutline : mCutLines)
449 pCutline->SwapChannels();
450 assert(CheckInvariants());
451}
452
454{
455 // Move right channel into result
456 newClip.mSequences.resize(1);
457 newClip.mSequences[0] = move(origClip.mSequences[1]);
458 // Delayed satisfaction of the class invariants after the empty construction
459 newClip.CheckInvariants();
460}
461
463 WaveClipHolders &myCutlines, WaveClipHolders &newCutlines)
464{
465 auto beginMe = myCutlines.begin(),
466 endMe = myCutlines.end();
467 auto iterNew = newCutlines.begin(),
468 endNew = newCutlines.end();
469 for_each(beginMe, endMe, [&](const auto &myCutline){
470 assert(iterNew != endNew);
471 const auto pNew = *iterNew;
472 TransferSequence(*myCutline, *pNew);
473 // Recursion!
474 FixSplitCutlines(myCutline->mCutLines, pNew->mCutLines);
475 ++iterNew;
476 });
477 assert(iterNew == endNew);
478}
479
480std::shared_ptr<WaveClip> WaveClip::SplitChannels()
481{
482 assert(NChannels() == 2);
483
484 // Make empty copies of this and all cutlines
485 CreateToken token{ true };
486 auto result = std::make_shared<WaveClip>(*this, GetFactory(), true, token);
487
488 // Move one Sequence
489 TransferSequence(*this, *result);
490
491 // Must also do that for cutlines, which must be in correspondence, because
492 // of the post of the constructor.
493 // And possibly too for cutlines inside of cutlines!
494 FixSplitCutlines(mCutLines, result->mCutLines);
495
496 // Fix attachments in the new clip and assert consistency conditions between
497 // the clip and its cutlines
498 result->Attachments::ForEach([](WaveClipListener &attachment){
499 attachment.Erase(0);
500 });
501 assert(result->CheckInvariants());
502
503 // This call asserts invariants for this clip
505
506 // Assert postconditions
507 assert(NChannels() == 1);
508 assert(result->NChannels() == 1);
509 return result;
510}
511
512void WaveClip::MakeStereo(WaveClip &&other, bool mustAlign)
513{
514 assert(NChannels() == 1);
515 assert(other.NChannels() == 1);
516 assert(GetSampleFormats() == other.GetSampleFormats());
517 assert(GetFactory() == other.GetFactory());
518 assert(!mustAlign || GetNumSamples() == other.GetNumSamples());
519
520 mCutLines.clear();
521 mSequences.resize(2);
522 mSequences[1] = move(other.mSequences[0]);
523
524 this->Attachments::ForCorresponding(other,
525 [mustAlign](WaveClipListener *pLeft, WaveClipListener *pRight){
526 // Precondition of callback from ClientData::Site
527 assert(pLeft && pRight);
528 pLeft->MakeStereo(std::move(*pRight), mustAlign);
529 });
530
531 if (mustAlign)
532 assert(StrongInvariant());
533 else
534 assert(CheckInvariants());
535}
536
538{
539 size_t result = 0;
540 for (size_t iChannel = 0; iChannel < NChannels(); ++iChannel)
541 result = std::max(result, mSequences[iChannel]->GetAppendBufferLen());
542 return result;
543}
544
546 const std::optional<double>& oldTempo, double newTempo)
547{
548 if (!mRawAudioTempo.has_value())
549 // When we have tempo detection ready (either by header-file
550 // read-up or signal analysis) we can use something smarter than that. In
551 // the meantime, use the tempo of the project when the clip is created as
552 // source tempo.
553 mRawAudioTempo = oldTempo.value_or(newTempo);
554
555 if (oldTempo.has_value())
556 {
557 const auto ratioChange = *oldTempo / newTempo;
558 mSequenceOffset *= ratioChange;
559 mTrimLeft *= ratioChange;
560 mTrimRight *= ratioChange;
561 StretchCutLines(ratioChange);
562 mEnvelope->RescaleTimesBy(ratioChange);
563 }
564 mProjectTempo = newTempo;
567}
568
570{
571 const auto pet = GetPlayEndTime();
572 if (to >= pet)
573 return;
574 const auto oldPlayDuration = pet - GetPlayStartTime();
575 const auto newPlayDuration = pet - to;
576 const auto ratioChange = newPlayDuration / oldPlayDuration;
577 mSequenceOffset = pet - (pet - mSequenceOffset) * ratioChange;
578 mTrimLeft *= ratioChange;
579 mTrimRight *= ratioChange;
580 mClipStretchRatio *= ratioChange;
581 mEnvelope->SetOffset(mSequenceOffset);
582 mEnvelope->RescaleTimesBy(ratioChange);
583 StretchCutLines(ratioChange);
586}
587
589{
590 const auto pst = GetPlayStartTime();
591 if (to <= pst)
592 return;
593 const auto oldPlayDuration = GetPlayEndTime() - pst;
594 const auto newPlayDuration = to - pst;
595 const auto ratioChange = newPlayDuration / oldPlayDuration;
596 StretchBy(ratioChange);
597}
598
599void WaveClip::StretchBy(double ratio)
600{
601 const auto pst = GetPlayStartTime();
602 mSequenceOffset = pst - mTrimLeft * ratio;
603 mTrimLeft *= ratio;
604 mTrimRight *= ratio;
605 mClipStretchRatio *= ratio;
606 mEnvelope->SetOffset(mSequenceOffset);
607 mEnvelope->RescaleTimesBy(ratio);
608 StretchCutLines(ratio);
611}
612
613void WaveClip::StretchCutLines(double ratioChange)
614{
615 for (const auto& cutline : mCutLines)
616 {
617 cutline->mSequenceOffset *= ratioChange;
618 cutline->mTrimLeft *= ratioChange;
619 cutline->mTrimRight *= ratioChange;
620 cutline->mClipStretchRatio *= ratioChange;
621 cutline->mEnvelope->RescaleTimesBy(ratioChange);
622 }
623}
624
626{
627 const auto dstSrcRatio =
628 mProjectTempo.has_value() && mRawAudioTempo.has_value() ?
630 1.0;
631 return mClipStretchRatio * dstSrcRatio;
632}
633
635{
636 return mCentShift;
637}
638
640WaveClip::SubscribeToCentShiftChange(std::function<void(int)> cb) const
641{
642 // Consider the list of subcribers as a mutable member that doesn't change
643 // real state
644 return const_cast<WaveClip*>(this)->
646 [cb](const CentShiftChange& cents) { cb(cents.newValue); });
647}
648
650 std::function<void(PitchAndSpeedPreset)> cb) const
651{
652 // Consider the list of subcribers as a mutable member that doesn't change
653 // real state
654 return const_cast<WaveClip*>(this)->
656 [cb](const PitchAndSpeedPresetChange& formant) {
657 cb(formant.newValue);
658 });
659}
660
662{
663 return StretchRatioEquals(other.GetStretchRatio()) &&
664 GetCentShift() == other.GetCentShift();
665}
666
668{
669 return !StretchRatioEquals(1.0) || GetCentShift() != 0;
670}
671
672bool WaveClip::StretchRatioEquals(double value) const
673{
675 1 + GetStretchRatio() - value);
676}
677
679{
680 // Assume only the weak invariant
681 sampleCount result = 0;
682 for (auto &pSequence: mSequences)
683 result = std::max(result, pSequence->GetNumSamples());
684 return result;
685}
686
688{
689 // All sequences have the same formats by class invariant
690 return mSequences[0]->GetSampleFormats();
691}
692
694{
695 return std::accumulate(mSequences.begin(), mSequences.end(), size_t{},
696 [](size_t acc, auto &pSequence){
697 return acc + pSequence->GetBlockArray().size(); });
698}
699
702{
703 return mSequences[0]->GetBestBlockSize(t);
704}
705
707{
708 return std::accumulate(mSequences.begin(), mSequences.end(), size_t{},
709 [](size_t acc, auto &pSequence){
710 return std::max(acc, pSequence->GetMaxBlockSize()); });
711}
712
714{
715 // All sequences have the same factory by class invariant
716 return mSequences[0]->GetFactory();
717}
718
719std::vector<std::unique_ptr<Sequence>> WaveClip::GetEmptySequenceCopies() const
720{
721 decltype(mSequences) newSequences;
722 newSequences.reserve(mSequences.size());
723 for (auto& pSequence : mSequences)
724 newSequences.push_back(std::make_unique<Sequence>(
725 pSequence->GetFactory(), pSequence->GetSampleFormats()));
726 return newSequences;
727}
728
730{
731 assert(ii < NChannels());
732 return mSequences[ii]->GetAppendBuffer();
733}
734
735void WaveClip::MarkChanged() noexcept // NOFAIL-GUARANTEE
736{
737 Attachments::ForEach(std::mem_fn(&WaveClipListener::MarkChanged));
738}
739
740std::pair<float, float> WaveClip::GetMinMax(size_t ii,
741 double t0, double t1, bool mayThrow) const
742{
743 assert(ii < NChannels());
744 t0 = std::max(t0, GetPlayStartTime());
745 t1 = std::min(t1, GetPlayEndTime());
746 if (t0 > t1) {
747 if (mayThrow)
749 return {
750 0.f, // harmless, but unused since Sequence::GetMinMax does not use these values
751 0.f // harmless, but unused since Sequence::GetMinMax does not use these values
752 };
753 }
754
755 if (t0 == t1)
756 return{ 0.f, 0.f };
757
758 auto s0 = TimeToSequenceSamples(t0);
759 auto s1 = TimeToSequenceSamples(t1);
760
761 return mSequences[ii]->GetMinMax(s0, s1 - s0, mayThrow);
762}
763
764float WaveClip::GetRMS(size_t ii, double t0, double t1, bool mayThrow) const
765{
766 assert(ii < NChannels());
767 if (t0 > t1) {
768 if (mayThrow)
770 return 0.f;
771 }
772
773 if (t0 == t1)
774 return 0.f;
775
776 auto s0 = TimeToSequenceSamples(t0);
777 auto s1 = TimeToSequenceSamples(t1);
778
779 return mSequences[ii]->GetRMS(s0, s1-s0, mayThrow);
780}
781
783 const std::function<void(size_t)> & progressReport)
784{
785 // This mutator does not require the strong invariant. It leaves sample
786 // counts unchanged in each sequence.
787
788 // Note: it is not necessary to do this recursively to cutlines.
789 // They get converted as needed when they are expanded.
790
791 Transaction transaction{ *this };
792
793 auto bChanged = mSequences[0]->ConvertToSampleFormat(format, progressReport);
794 for (size_t ii = 1, width = NChannels(); ii < width; ++ii) {
795 bool alsoChanged =
796 mSequences[ii]->ConvertToSampleFormat(format, progressReport);
797 // Class invariant implies:
798 assert(bChanged == alsoChanged);
799 }
800 if (bChanged)
801 MarkChanged();
802
803 transaction.Commit();
804}
805
808{
809 // The envelope time points account for stretching.
810 const auto len = GetNumSamples().as_double() * GetStretchRatio() / mRate;
811 if (len != mEnvelope->GetTrackLen())
812 mEnvelope->SetTrackLen(len, 1.0 / GetRate());
813}
814
816std::shared_ptr<SampleBlock>
818 constSamplePtr buffer, sampleFormat format, size_t len)
819{
820 assert(iChannel < NChannels());
821 return mSequences[iChannel]->AppendNewBlock(buffer, format, len);
822}
823
825std::shared_ptr<SampleBlock>
827{
828 // This is a special use function for legacy files only and this assertion
829 // does not need to be relaxed. The clip is in a still unzipped track.
830 assert(NChannels() == 1);
831 return AppendToChannel(0, buffer, format, len);
832}
833
836 const std::shared_ptr<SampleBlock> &pBlock)
837{
838 // This is a special use function for legacy files only and this assertion
839 // does not need to be relaxed. The clip is in a still unzipped track.
840 assert(NChannels() == 1);
841 mSequences[0]->AppendSharedBlock( pBlock );
842}
843
844bool WaveClip::Append(size_t iChannel, const size_t nChannels,
846 size_t len, unsigned int stride, sampleFormat effectiveFormat)
847{
848 assert(iChannel < NChannels());
849 assert(iChannel + nChannels <= NChannels());
850
851 // No requirement or promise of the strong invariant, and therefore no
852 // need for Transaction
853
854 //wxLogDebug(wxT("Append: len=%lli"), (long long) len);
855
856 bool appended = false;
857 for (size_t ii = 0; ii < nChannels; ++ii)
858 appended = mSequences[iChannel + ii]->Append(
859 buffers[ii], format, len, stride, effectiveFormat)
860 || appended;
861
862 // use No-fail-guarantee
864 MarkChanged();
865
866 return appended;
867}
868
870 size_t len, unsigned int stride, sampleFormat effectiveFormat)
871{
873
874 Transaction transaction{ *this };
875
876 //wxLogDebug(wxT("Append: len=%lli"), (long long) len);
877
878 size_t ii = 0;
879 bool appended = false;
880 for (auto &pSequence : mSequences)
881 appended =
882 pSequence->Append(buffers[ii++], format, len, stride, effectiveFormat)
883 || appended;
884
885 transaction.Commit();
886 // use No-fail-guarantee
888 MarkChanged();
889
890 return appended;
891}
892
894{
895 // Does not require or guarantee the strong invariant
896
897 //wxLogDebug(wxT("WaveClip::Flush"));
898 //wxLogDebug(wxT(" mAppendBufferLen=%lli"), (long long) mAppendBufferLen);
899 //wxLogDebug(wxT(" previous sample count %lli"), (long long) mSequence->GetNumSamples());
900
901 if (GreatestAppendBufferLen() > 0) {
902
903 Transaction transaction{ *this };
904
905 for (auto &pSequence : mSequences)
906 pSequence->Flush();
907
908 transaction.Commit();
909
910 // No-fail operations
912 MarkChanged();
913 }
914
915 //wxLogDebug(wxT("now sample count %lli"), (long long) mSequence->GetNumSamples());
916}
917
919{
920 if (NChannels() < 2)
921 return;
922 // Be sure of consistency of sample counts
923 // We may be here because the drive can't hold another megabyte, but
924 // note that InsertSilence makes silent blocks that don't occupy
925 // space in the database table of blocks.
926 // (However autosave may want to rewrite the document blob, so this solution
927 // may yet not be perfect.)
928 Transaction transaction{ *this };
929 const auto maxSamples = GetNumSamples();
930 for (const auto &pSequence: mSequences) {
931 const auto numSamples = pSequence->GetNumSamples();
932 if (pSequence->GetNumSamples() != maxSamples)
933 pSequence->InsertSilence(numSamples, maxSamples - numSamples);
934 }
935 transaction.Commit();
936}
937
938static constexpr auto Offset_attr = "offset";
939static constexpr auto TrimLeft_attr = "trimLeft";
940static constexpr auto TrimRight_attr = "trimRight";
941static constexpr auto CentShiftAttr = "centShift";
942static constexpr auto PitchAndSpeedPreset_attr = "pitchAndSpeedPreset";
943static constexpr auto RawAudioTempo_attr = "rawAudioTempo";
944static constexpr auto ClipStretchRatio_attr = "clipStretchRatio";
945static constexpr auto Name_attr = "name";
946
947bool WaveClip::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
948{
949 if (tag == WaveClip_tag)
950 {
951 double dblValue;
952 long longValue;
953 for (auto pair : attrs)
954 {
955 auto attr = pair.first;
956 auto value = pair.second;
957
958 if (attr == Offset_attr)
959 {
960 if (!value.TryGet(dblValue))
961 return false;
962 SetSequenceStartTime(dblValue);
963 }
964 else if (attr == TrimLeft_attr)
965 {
966 if (!value.TryGet(dblValue))
967 return false;
968 SetTrimLeft(dblValue);
969 }
970 else if (attr == TrimRight_attr)
971 {
972 if (!value.TryGet(dblValue))
973 return false;
974 SetTrimRight(dblValue);
975 }
976 else if (attr == CentShiftAttr)
977 {
978 if (!value.TryGet(dblValue))
979 return false;
980 mCentShift = dblValue;
981 }
982 else if (attr == PitchAndSpeedPreset_attr)
983 {
984 if (!value.TryGet(longValue))
985 return false;
986 mPitchAndSpeedPreset = static_cast<PitchAndSpeedPreset>(longValue);
987 }
988 else if (attr == RawAudioTempo_attr)
989 {
990 if (!value.TryGet(dblValue))
991 return false;
992 if (dblValue == 0)
993 mRawAudioTempo.reset();
994 else
995 mRawAudioTempo = dblValue;
996 }
997 else if (attr == ClipStretchRatio_attr)
998 {
999 if (!value.TryGet(dblValue))
1000 return false;
1001 mClipStretchRatio = dblValue;
1002 }
1003 else if (attr == Name_attr)
1004 {
1005 if(value.IsStringView())
1006 SetName(value.ToWString());
1007 }
1008 else if (Attachments::FindIf(
1009 [&](WaveClipListener &listener){
1010 return listener.HandleXMLAttribute(attr, value); }
1011 ))
1012 ;
1013 }
1014 return true;
1015 }
1016
1017 return false;
1018}
1019
1020void WaveClip::HandleXMLEndTag(const std::string_view& tag)
1021{
1022 // All blocks were deserialized into new sequences; remove the one made
1023 // by the constructor which remains empty.
1024 mSequences.erase(mSequences.begin());
1025 mSequences.shrink_to_fit();
1026 if (tag == WaveClip_tag)
1028 // A proof of this assertion assumes that nothing has happened since
1029 // construction of this, besides calls to the other deserialization
1030 // functions
1031 assert(CheckInvariants());
1032}
1033
1034XMLTagHandler *WaveClip::HandleXMLChild(const std::string_view& tag)
1035{
1036 auto &pFirst = mSequences[0];
1037 if (tag == Sequence::Sequence_tag) {
1038 // Push back a new sequence prototyped from the empty sequence made
1039 // by the constructor. See also HandleXMLEndTag above.
1040 // Assume sequences were serialized in channel iteration order.
1041 mSequences.push_back(std::make_unique<Sequence>(
1042 pFirst->GetFactory(), pFirst->GetSampleFormats()));
1043 return mSequences.back().get();
1044 }
1045 else if (tag == "envelope")
1046 return mEnvelope.get();
1047 else if (tag == WaveClip_tag)
1048 {
1049 // Nested wave clips are cut lines
1050 auto format = pFirst->GetSampleFormats().Stored();
1051 // The format is not stored in WaveClip itself but passed to
1052 // Sequence::Sequence; but then the Sequence will deserialize format
1053 // again
1054 mCutLines.push_back(
1055 std::make_shared<WaveClip>(
1056 // Make only one channel now, but recursive deserialization
1057 // increases the width later
1058 1, pFirst->GetFactory(),
1059 format, mRate));
1060 return mCutLines.back().get();
1061 }
1062 else
1063 return nullptr;
1064}
1065
1066void WaveClip::WriteXML(size_t ii, XMLWriter &xmlFile) const
1067// may throw
1068{
1069 assert(ii < NChannels());
1070
1071 if (GetSequenceSamplesCount() <= 0)
1072 // Oops, I'm empty? How did that happen? Anyway, I do nothing but causing
1073 // problems, don't save me.
1074 return;
1075
1076 xmlFile.StartTag(WaveClip_tag);
1077 xmlFile.WriteAttr(Offset_attr, mSequenceOffset, 8);
1078 xmlFile.WriteAttr(TrimLeft_attr, mTrimLeft, 8);
1079 xmlFile.WriteAttr(TrimRight_attr, mTrimRight, 8);
1080 xmlFile.WriteAttr(CentShiftAttr, mCentShift);
1081 xmlFile.WriteAttr(PitchAndSpeedPreset_attr,
1082 static_cast<long>(mPitchAndSpeedPreset));
1083 xmlFile.WriteAttr(RawAudioTempo_attr, mRawAudioTempo.value_or(0.), 8);
1084 xmlFile.WriteAttr(ClipStretchRatio_attr, mClipStretchRatio, 8);
1085 xmlFile.WriteAttr(Name_attr, mName);
1086 Attachments::ForEach([&](const WaveClipListener &listener){
1087 listener.WriteXMLAttributes(xmlFile);
1088 });
1089
1090 mSequences[ii]->WriteXML(xmlFile);
1091 mEnvelope->WriteXML(xmlFile);
1092
1093 for (const auto &clip: mCutLines)
1094 clip->WriteXML(ii, xmlFile);
1095
1096 xmlFile.EndTag(WaveClip_tag);
1097}
1098
1100bool WaveClip::Paste(double t0, const WaveClip& o)
1101{
1102 const WaveClip *pOther = &o;
1103 WaveClipHolder dup;
1104 if (!o.StrongInvariant()) {
1105 assert(false); // precondition not honored
1106 // But try to repair it and continue in release
1107 dup = std::make_shared<WaveClip>(o, o.GetFactory(), true);
1108 dup->RepairChannels();
1109 pOther = dup.get();
1110 }
1111 auto &other = *pOther;
1112
1113 if (NChannels() != other.NChannels())
1114 // post is satisfied
1115 return false;
1116
1117 if (GetSequenceSamplesCount() == 0)
1118 {
1119 // Empty clip: we're flexible and adopt the other's stretching.
1120 mRawAudioTempo = other.mRawAudioTempo;
1121 mClipStretchRatio = other.mClipStretchRatio;
1122 mProjectTempo = other.mProjectTempo;
1123 }
1124 else if (GetStretchRatio() != other.GetStretchRatio())
1125 // post is satisfied
1126 return false;
1127
1128 StrongInvariantScope scope{ *this };
1129
1130 Transaction transaction{ *this };
1131
1132 const bool clipNeedsResampling = other.mRate != mRate;
1133 const bool clipNeedsNewFormat =
1134 other.GetSampleFormats().Stored() != GetSampleFormats().Stored();
1135 std::shared_ptr<WaveClip> newClip;
1136
1137 t0 = std::clamp(t0, GetPlayStartTime(), GetPlayEndTime());
1138 // Delay the finish of the clearing of this clip
1139 ClearSequenceFinisher finisher;
1140
1141 //seems like edge cases cannot happen, see WaveTrack::PasteWaveTrack
1142 auto &factory = GetFactory();
1143 if (t0 == GetPlayStartTime())
1144 {
1145 finisher = ClearSequence(GetSequenceStartTime(), t0);
1146 SetTrimLeft(other.GetTrimLeft());
1147
1148 auto copy = std::make_shared<WaveClip>(other, factory, true);
1149 copy->ClearSequence(copy->GetPlayEndTime(), copy->GetSequenceEndTime())
1150 .Commit();
1151 newClip = std::move(copy);
1152 }
1153 else if (t0 == GetPlayEndTime())
1154 {
1156 SetTrimRight(other.GetTrimRight());
1157
1158 auto copy = std::make_shared<WaveClip>(other, factory, true);
1159 copy->ClearSequence(copy->GetSequenceStartTime(), copy->GetPlayStartTime())
1160 .Commit();
1161 newClip = std::move(copy);
1162 }
1163 else
1164 {
1165 newClip = std::make_shared<WaveClip>(other, factory, true);
1166 newClip->ClearSequence(newClip->GetPlayEndTime(), newClip->GetSequenceEndTime())
1167 .Commit();
1168 newClip->ClearSequence(newClip->GetSequenceStartTime(), newClip->GetPlayStartTime())
1169 .Commit();
1170 newClip->SetTrimLeft(0);
1171 newClip->SetTrimRight(0);
1172 }
1173
1174 if (clipNeedsResampling || clipNeedsNewFormat)
1175 {
1176 auto copy = std::make_shared<WaveClip>(*newClip.get(), factory, true);
1177
1178 if (clipNeedsResampling)
1179 // The other clip's rate is different from ours, so resample
1180 copy->Resample(mRate);
1181
1182 if (clipNeedsNewFormat)
1183 // Force sample formats to match.
1184 copy->ConvertToSampleFormat(GetSampleFormats().Stored());
1185 newClip = std::move(copy);
1186 }
1187
1188 // Paste cut lines contained in pasted clip
1189 WaveClipHolders newCutlines;
1190 for (const auto &cutline: newClip->mCutLines)
1191 {
1192 auto cutlineCopy = std::make_shared<WaveClip>(*cutline, factory,
1193 // Recursively copy cutlines of cutlines. They don't need
1194 // their offsets adjusted.
1195 true);
1196 cutlineCopy->ShiftBy(t0 - GetSequenceStartTime());
1197 newCutlines.push_back(std::move(cutlineCopy));
1198 }
1199
1201
1202 // Because newClip was made above as a copy of (a copy of) other
1203 assert(other.NChannels() == newClip->NChannels());
1204 // And other has the same width as this, so this loop is safe
1205 // Assume Strong-guarantee from Sequence::Paste
1206 for (size_t ii = 0, width = NChannels(); ii < width; ++ii)
1207 mSequences[ii]->Paste(s0, newClip->mSequences[ii].get());
1208
1209 // Assume No-fail-guarantee in the remaining
1210
1211 finisher.Commit();
1212 transaction.Commit();
1213 MarkChanged();
1214
1215 const auto sampleTime = 1.0 / GetRate();
1216 const auto timeOffsetInEnvelope =
1218 mEnvelope->PasteEnvelope(
1219 timeOffsetInEnvelope, newClip->mEnvelope.get(), sampleTime);
1220 OffsetCutLines(t0, newClip->GetPlayEndTime() - newClip->GetPlayStartTime());
1221
1222 for (auto &holder : newCutlines)
1223 mCutLines.push_back(std::move(holder));
1224
1225 return true;
1226}
1227
1229void WaveClip::InsertSilence( double t, double len, double *pEnvelopeValue )
1230{
1231 StrongInvariantScope scope{ *this };
1232 Transaction transaction{ *this };
1233 ClearSequenceFinisher finisher;
1234
1235 if (t == GetPlayStartTime() && t > GetSequenceStartTime())
1236 finisher = ClearSequence(GetSequenceStartTime(), t);
1237 else if (t == GetPlayEndTime() && t < GetSequenceEndTime()) {
1238 finisher = ClearSequence(t, GetSequenceEndTime());
1239 SetTrimRight(.0);
1240 }
1241
1242 const auto s0 = TimeToSequenceSamples(t);
1243 const auto slen = TimeToSamples(len);
1244
1245 // use Strong-guarantee
1246 for (auto &pSequence : mSequences)
1247 pSequence->InsertSilence(s0, slen);
1248
1249 // use No-fail-guarantee in the rest
1250 finisher.Commit();
1251 transaction.Commit();
1252
1253 OffsetCutLines(t, len);
1254
1255 const auto sampleTime = 1.0 / GetRate();
1256 auto &envelope = GetEnvelope();
1257 if ( pEnvelopeValue ) {
1258
1259 // Preserve limit value at the end
1260 auto oldLen = envelope.GetTrackLen();
1261 auto newLen = oldLen + len;
1262 envelope.Cap( sampleTime );
1263
1264 // Ramp across the silence to the given value
1265 envelope.SetTrackLen( newLen, sampleTime );
1266 envelope.InsertOrReplace
1267 ( envelope.GetOffset() + newLen, *pEnvelopeValue );
1268 }
1269 else
1270 envelope.InsertSpace( t, len );
1271
1272 MarkChanged();
1273}
1274
1276void WaveClip::AppendSilence( double len, double envelopeValue )
1277{
1278 auto t = GetPlayEndTime();
1279 InsertSilence( t, len, &envelopeValue );
1280}
1281
1283void WaveClip::Clear(double t0, double t1)
1284{
1285 auto st0 = t0;
1286 auto st1 = t1;
1287 auto offset = .0;
1288 if (st0 <= GetPlayStartTime())
1289 {
1290 offset = (t0 - GetPlayStartTime()) + GetTrimLeft();
1291 st0 = GetSequenceStartTime();
1292
1293 SetTrimLeft(.0);
1294 }
1295 if (st1 >= GetPlayEndTime())
1296 {
1297 st1 = GetSequenceEndTime();
1298 SetTrimRight(.0);
1299 }
1300 Transaction transaction{ *this };
1301 ClearSequence(st0, st1)
1302 .Commit();
1303 transaction.Commit();
1304 MarkChanged();
1305
1306 if (offset != .0)
1307 ShiftBy(offset);
1308}
1309
1311{
1312 if (t > GetPlayStartTime() && t < GetPlayEndTime())
1313 {
1314 Transaction transaction{ *this };
1316 .Commit();
1317 transaction.Commit();
1318 SetTrimLeft(.0);
1320 MarkChanged();
1321 }
1322}
1323
1325{
1326 if (t > GetPlayStartTime() && t < GetPlayEndTime())
1327 {
1328 Transaction transaction{ *this };
1330 .Commit();
1331 transaction.Commit();
1332 SetTrimRight(.0);
1333 MarkChanged();
1334 }
1335}
1336
1338{
1339 StrongInvariantScope scope{ *this };
1340
1341 auto clip_t0 = std::max(t0, GetSequenceStartTime());
1342 auto clip_t1 = std::min(t1, GetSequenceEndTime());
1343
1344 auto s0 = TimeToSequenceSamples(clip_t0);
1345 auto s1 = TimeToSequenceSamples(clip_t1);
1346
1347 if (s0 == s1)
1348 return {};
1349
1350 // use Strong-guarantee
1351 for (auto &pSequence : mSequences)
1352 pSequence->Delete(s0, s1 - s0);
1353
1354 return { this, t0, t1, clip_t0, clip_t1 };
1355}
1356
1358{
1359 if (!pClip || !committed)
1360 return;
1361
1362 // use No-fail-guarantee in the remaining
1363
1364 // msmeyer
1365 //
1366 // Delete all cutlines that are within the given area, if any.
1367 //
1368 // Note that when cutlines are active, two functions are used:
1369 // Clear() and ClearAndAddCutLine(). ClearAndAddCutLine() is called
1370 // whenever the user directly calls a command that removes some audio, e.g.
1371 // "Cut" or "Clear" from the menu. This command takes care about recursive
1372 // preserving of cutlines within clips. Clear() is called when internal
1373 // operations want to remove audio. In the latter case, it is the right
1374 // thing to just remove all cutlines within the area.
1375 //
1376
1377 // May DELETE as we iterate, so don't use range-for
1378 for (auto it = pClip->mCutLines.begin(); it != pClip->mCutLines.end();)
1379 {
1380 WaveClip* clip = it->get();
1381 double cutlinePosition =
1383 if (cutlinePosition >= t0 && cutlinePosition <= t1)
1384 {
1385 // This cutline is within the area, DELETE it
1386 it = pClip->mCutLines.erase(it);
1387 }
1388 else
1389 {
1390 if (cutlinePosition >= t1)
1391 {
1392 clip->ShiftBy(clip_t0 - clip_t1);
1393 }
1394 ++it;
1395 }
1396 }
1397
1398 // Collapse envelope
1399 auto sampleTime = 1.0 / pClip->GetRate();
1400 pClip->GetEnvelope().CollapseRegion(t0, t1, sampleTime);
1401}
1402
1406void WaveClip::ClearAndAddCutLine(double t0, double t1)
1407{
1408 StrongInvariantScope scope{ *this };
1409 if (t0 > GetPlayEndTime() || t1 < GetPlayStartTime() || CountSamples(t0, t1) == 0)
1410 return; // no samples to remove
1411
1412 Transaction transaction{ *this };
1413
1414 const double clip_t0 = std::max( t0, GetPlayStartTime() );
1415 const double clip_t1 = std::min( t1, GetPlayEndTime() );
1416
1417 auto newClip = std::make_shared<WaveClip>(
1418 *this, GetFactory(), true, clip_t0, clip_t1);
1419 if(t1 < GetPlayEndTime())
1420 {
1421 newClip->ClearSequence(t1, newClip->GetSequenceEndTime())
1422 .Commit();
1423 newClip->SetTrimRight(.0);
1424 }
1425 if(t0 > GetPlayStartTime())
1426 {
1427 newClip->ClearSequence(newClip->GetSequenceStartTime(), t0)
1428 .Commit();
1429 newClip->SetTrimLeft(.0);
1430 }
1431
1432 newClip->SetSequenceStartTime( clip_t0 - GetSequenceStartTime() );
1433
1434 // Remove cutlines from this clip that were in the selection, shift
1435 // left those that were after the selection
1436 // May DELETE as we iterate, so don't use range-for
1437 for (auto it = mCutLines.begin(); it != mCutLines.end();)
1438 {
1439 WaveClip* clip = it->get();
1440 double cutlinePosition = GetSequenceStartTime() + clip->GetSequenceStartTime();
1441 if (cutlinePosition >= t0 && cutlinePosition <= t1)
1442 it = mCutLines.erase(it);
1443 else
1444 {
1445 if (cutlinePosition >= t1)
1446 {
1447 clip->ShiftBy(clip_t0 - clip_t1);
1448 }
1449 ++it;
1450 }
1451 }
1452
1453 // Clear actual audio data
1454 auto s0 = TimeToSequenceSamples(t0);
1455 auto s1 = TimeToSequenceSamples(t1);
1456
1457 // use Weak-guarantee
1458 for (auto &pSequence : mSequences)
1459 pSequence->Delete(s0, s1-s0);
1460
1461 // Collapse envelope
1462 auto sampleTime = 1.0 / GetRate();
1463 GetEnvelope().CollapseRegion( t0, t1, sampleTime );
1464
1465 transaction.Commit();
1466 MarkChanged();
1467 AddCutLine(move(newClip));
1468}
1469
1471{
1472 assert(NChannels() == pClip->NChannels());
1473 mCutLines.push_back(move(pClip));
1474 // New clip is assumed to have correct width
1475 assert(CheckInvariants());
1476}
1477
1478bool WaveClip::FindCutLine(double cutLinePosition,
1479 double* cutlineStart /* = NULL */,
1480 double* cutlineEnd /* = NULL */) const
1481{
1482 for (const auto &cutline: mCutLines)
1483 {
1484 if (fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001)
1485 {
1486 auto startTime = GetSequenceStartTime() + cutline->GetSequenceStartTime();
1487 if (cutlineStart)
1488 *cutlineStart = startTime;
1489 if (cutlineEnd)
1490 *cutlineEnd = startTime + cutline->SamplesToTime(cutline->GetVisibleSampleCount());
1491 return true;
1492 }
1493 }
1494
1495 return false;
1496}
1497
1499void WaveClip::ExpandCutLine(double cutLinePosition)
1500{
1501 auto end = mCutLines.end();
1502 auto it = std::find_if( mCutLines.begin(), end,
1503 [&](const WaveClipHolder &cutline) {
1504 return fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001;
1505 } );
1506
1507 if ( it != end ) {
1508 auto *cutline = it->get();
1509 // assume Strong-guarantee from Paste
1510
1511 // Envelope::Paste takes offset into account, WaveClip::Paste doesn't!
1512 // Do this to get the right result:
1513 cutline->mEnvelope->SetOffset(0);
1514 bool success = Paste(
1515 GetSequenceStartTime() + cutline->GetSequenceStartTime(), *cutline);
1516 assert(success); // class invariant promises cutlines have correct width
1517
1518 // Now erase the cutline,
1519 // but be careful to find it again, because Paste above may
1520 // have modified the array of cutlines (if our cutline contained
1521 // another cutline!), invalidating the iterator we had.
1522 end = mCutLines.end();
1523 it = std::find_if(mCutLines.begin(), end,
1524 [=](const WaveClipHolder &p) { return p.get() == cutline; });
1525 if (it != end)
1526 mCutLines.erase(it); // deletes cutline!
1527 else {
1528 wxASSERT(false);
1529 }
1530 }
1531}
1532
1533bool WaveClip::RemoveCutLine(double cutLinePosition)
1534{
1535 for (auto it = mCutLines.begin(); it != mCutLines.end(); ++it)
1536 {
1537 const auto &cutline = *it;
1538 //std::numeric_limits<double>::epsilon() or (1.0 / static_cast<double>(mRate))?
1539 if (fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001)
1540 {
1541 mCutLines.erase(it); // deletes cutline!
1542 return true;
1543 }
1544 }
1545
1546 return false;
1547}
1548
1550void WaveClip::OffsetCutLines(double t0, double len)
1551{
1552 for (const auto &cutLine : mCutLines)
1553 {
1554 if (GetSequenceStartTime() + cutLine->GetSequenceStartTime() >= t0)
1555 cutLine->ShiftBy(len);
1556 }
1557}
1558
1559void WaveClip::CloseLock() noexcept
1560{
1561 // Don't need a Transaction for noexcept operations
1562 for (auto &pSequence : mSequences)
1563 pSequence->CloseLock();
1564 for (const auto &cutline: mCutLines)
1565 cutline->CloseLock();
1566}
1567
1568void WaveClip::SetRate(int rate)
1569{
1570 const auto trimLeftSampleNum = TimeToSamples(mTrimLeft);
1571 const auto trimRightSampleNum = TimeToSamples(mTrimRight);
1572 auto ratio = static_cast<double>(mRate) / rate;
1573 mRate = rate;
1574 mTrimLeft = SamplesToTime(trimLeftSampleNum);
1575 mTrimRight = SamplesToTime(trimRightSampleNum);
1576 const auto newLength =
1578 mEnvelope->RescaleTimes(newLength);
1579 MarkChanged();
1581}
1582
1584{
1585 mRawAudioTempo = tempo;
1586}
1587
1589{
1590 if (
1593 return false;
1594 mCentShift = cents;
1596 return true;
1597}
1598
1600{
1604}
1605
1607{
1608 return mPitchAndSpeedPreset;
1609}
1610
1613{
1614 // This mutator does not require the strong invariant.
1615
1616 // Note: it is not necessary to do this recursively to cutlines.
1617 // They get resampled as needed when they are expanded.
1618
1619 if (rate == mRate)
1620 return; // Nothing to do
1621
1622 // This function does its own RAII without a Transaction
1623
1624 double factor = (double)rate / (double)mRate;
1625 ::Resample resample(true, factor, factor); // constant rate resampling
1626
1627 const size_t bufsize = 65536;
1628 Floats inBuffer{ bufsize };
1629 Floats outBuffer{ bufsize };
1630 sampleCount pos = 0;
1631 bool error = false;
1632 int outGenerated = 0;
1633 const auto numSamples = GetNumSamples();
1634
1635 // These sequences are appended to below
1636 auto newSequences = GetEmptySequenceCopies();
1637
1643 while (pos < numSamples || outGenerated > 0) {
1644 const auto inLen = limitSampleBufferSize( bufsize, numSamples - pos );
1645
1646 bool isLast = ((pos + inLen) == numSamples);
1647
1648 auto ppNewSequence = newSequences.begin();
1649 std::optional<std::pair<size_t, size_t>> results{};
1650 for (auto &pSequence : mSequences) {
1651 auto &pNewSequence = *ppNewSequence++;
1652 if (!pSequence->Get((samplePtr)inBuffer.get(), floatSample, pos, inLen, true))
1653 {
1654 error = true;
1655 break;
1656 }
1657
1658 // Expect the same results for all channels, or else fail
1659 auto newResults = resample.Process(factor, inBuffer.get(), inLen,
1660 isLast, outBuffer.get(), bufsize);
1661 if (!results)
1662 results.emplace(newResults);
1663 else if (*results != newResults) {
1664 error = true;
1665 break;
1666 }
1667
1668 outGenerated = results->second;
1669 if (outGenerated < 0) {
1670 error = true;
1671 break;
1672 }
1673
1674 pNewSequence->Append((samplePtr)outBuffer.get(), floatSample,
1675 outGenerated, 1,
1676 widestSampleFormat /* computed samples need dither */
1677 );
1678 }
1679 if (results)
1680 pos += results->first;
1681
1682 if (progress)
1683 {
1684 auto updateResult = progress->Poll(
1685 pos.as_long_long(),
1686 numSamples.as_long_long()
1687 );
1688 error = (updateResult != BasicUI::ProgressResult::Success);
1689 if (error)
1690 throw UserException{};
1691 }
1692 }
1693
1694 if (error)
1697 XO("Resampling failed."),
1698 XO("Warning"),
1699 "Error:_Resampling"
1700 };
1701 else
1702 {
1703 // Use No-fail-guarantee in these steps
1704 mSequences = move(newSequences);
1705 mRate = rate;
1706 Flush();
1707 Attachments::ForEach( std::mem_fn( &WaveClipListener::Invalidate ) );
1708 MarkChanged();
1709 }
1710}
1711
1712void WaveClip::SetName(const wxString& name)
1713{
1714 mName = name;
1715}
1716
1717const wxString& WaveClip::GetName() const
1718{
1719 return mName;
1720}
1721
1723{
1724 return sampleCount(floor(time * mRate / GetStretchRatio() + 0.5));
1725}
1726
1727double WaveClip::SamplesToTime(sampleCount s) const noexcept
1728{
1729 return s.as_double() * GetStretchRatio() / mRate;
1730}
1731
1732double WaveClip::SnapToTrackSample(double t) const noexcept
1733{
1734 return std::round(t * mRate) / mRate;
1735}
1736
1738{
1739 StrongInvariantScope scope{ *this };
1740 const auto start = TimeToSamples(mTrimLeft) + offset;
1741 Transaction transaction{ *this };
1742 for (auto &pSequence : mSequences)
1743 pSequence->SetSilence(start, length);
1744 transaction.Commit();
1745 MarkChanged();
1746}
1747
1749{
1750 return GetNumSamples() * NChannels();
1751}
1752
1753double WaveClip::GetPlayStartTime() const noexcept
1754{
1756}
1757
1759{
1761}
1762
1764{
1765 const auto numSamples = GetNumSamples();
1766 double maxLen = mSequenceOffset +
1767 ((numSamples + GreatestAppendBufferLen()).as_double()) *
1769 mTrimRight;
1770 // JS: calculated value is not the length;
1771 // it is a maximum value and can be negative; no clipping to 0
1772 return SnapToTrackSample(maxLen);
1773}
1774
1776{
1777 return GetPlayEndTime() - GetPlayStartTime();
1778}
1779
1781{
1782 return std::floor(GetPlayDuration() * mRate + 0.5) < 2.0;
1783}
1784
1786{
1787 return sampleCount { GetPlayStartTime() * mRate + 0.5 };
1788}
1789
1791{
1792 return sampleCount { GetPlayEndTime() * mRate + 0.5 };
1793}
1794
1796{
1797 return GetNumSamples()
1799}
1800
1801void WaveClip::SetTrimLeft(double trim)
1802{
1803 mTrimLeft = std::max(.0, trim);
1804}
1805
1806double WaveClip::GetTrimLeft() const noexcept
1807{
1808 return mTrimLeft;
1809}
1810
1811void WaveClip::SetTrimRight(double trim)
1812{
1813 mTrimRight = std::max(.0, trim);
1814}
1815
1816double WaveClip::GetTrimRight() const noexcept
1817{
1818 return mTrimRight;
1819}
1820
1821void WaveClip::TrimLeft(double deltaTime)
1822{
1823 SetTrimLeft(mTrimLeft + deltaTime);
1824}
1825
1826void WaveClip::TrimRight(double deltaTime)
1827{
1828 SetTrimRight(mTrimRight + deltaTime);
1829}
1830
1832{
1833 assert(mRawAudioTempo.has_value());
1834 if (!mRawAudioTempo.has_value())
1835 return;
1836 const auto secondsPerQuarter = 60 * GetStretchRatio() / *mRawAudioTempo;
1837 // MH https://github.com/audacity/audacity/issues/5878: Clip boundaries are
1838 // quantized to the sample period. Music durations aren't, though.
1839 // `quarters` was probably chosen such that the clip ends exactly at some
1840 // musical grid snapping point. However, if we right-trim by `quarters`,
1841 // the clip's play end time might be rounded up to the next sample period,
1842 // overlapping the next snapping point on the musical grid. We don't want
1843 // this, or it would disturb music producers who want to horizontally
1844 // duplicate loops.
1845 const auto quantizedTrim =
1846 std::ceil(quarters * secondsPerQuarter * GetRate()) / GetRate();
1847 TrimRight(quantizedTrim);
1848}
1849
1851{
1852 mTrimLeft =
1855}
1856
1858{
1859 const auto endTime = SnapToTrackSample(GetSequenceEndTime());
1860 mTrimRight = endTime - std::clamp(to, GetPlayStartTime(), endTime);
1861}
1862
1863double WaveClip::GetSequenceStartTime() const noexcept
1864{
1865 // JS: mSequenceOffset is the minimum value and it is returned; no clipping to 0
1866 // Do we need to `SnapToTrackSample` before returning?
1867 return mSequenceOffset;
1868}
1869
1871{
1872 mSequenceOffset = startTime;
1873 mEnvelope->SetOffset(startTime);
1874}
1875
1877{
1878 const auto numSamples = GetNumSamples();
1879 double maxLen = GetSequenceStartTime() +
1880 numSamples.as_double() * GetStretchRatio() / mRate;
1881 return maxLen;
1882}
1883
1885{
1887}
1888
1889void WaveClip::ShiftBy(double delta) noexcept
1890{
1892 MarkChanged();
1893}
1894
1895bool WaveClip::SplitsPlayRegion(double t) const
1896{
1897 return GetPlayStartTime() < t && t < GetPlayEndTime();
1898}
1899
1900bool WaveClip::WithinPlayRegion(double t) const
1901{
1902 return GetPlayStartTime() <= t && t < GetPlayEndTime();
1903}
1904
1905bool WaveClip::EntirelyWithinPlayRegion(double t0, double t1) const
1906{
1907 assert(t0 <= t1);
1908 // t1 is the open end of the interval, hence it's ok if it's equal to the
1909 // open end of the play region.
1910 return !BeforePlayRegion(t0) && t1 <= GetPlayEndTime();
1911}
1912
1913bool WaveClip::PartlyWithinPlayRegion(double t0, double t1) const
1914{
1915 assert(t0 <= t1);
1916 return WithinPlayRegion(t0) != WithinPlayRegion(t1);
1917}
1918
1919bool WaveClip::IntersectsPlayRegion(double t0, double t1) const
1920{
1921 assert(t0 <= t1);
1922 // t1 is the open end of the interval, so it must be excluded from the closed
1923 // begin of the play region.
1924 return t0 < GetPlayEndTime() && GetPlayStartTime() < t1;
1925}
1926
1927bool WaveClip::CoversEntirePlayRegion(double t0, double t1) const
1928{
1929 assert(t0 <= t1);
1930 return t0 <= GetPlayStartTime() && GetPlayEndTime() <= t1;
1931}
1932
1933bool WaveClip::BeforePlayRegion(double t) const
1934{
1935 return t < GetPlayStartTime();
1936}
1937
1939{
1940 return t <= GetPlayStartTime();
1941}
1942
1943bool WaveClip::AfterPlayRegion(double t) const
1944{
1945 return GetPlayEndTime() <= t;
1946}
1947
1948sampleCount WaveClip::CountSamples(double t0, double t1) const
1949{
1950 if(t0 < t1)
1951 {
1952 t0 = std::max(t0, GetPlayStartTime());
1953 t1 = std::min(t1, GetPlayEndTime());
1954 const auto s0 = TimeToSamples(t0 - GetPlayStartTime());
1955 const auto s1 = TimeToSamples(t1 - GetPlayStartTime());
1956 return s1 - s0;
1957 }
1958 return { 0 };
1959}
1960
1962{
1963 if (t < GetSequenceStartTime())
1964 return 0;
1965 else if (t > GetSequenceEndTime())
1966 return GetNumSamples();
1967 return TimeToSamples(t - GetSequenceStartTime());
1968}
1969
1971{
1972 const auto width = NChannels();
1973 auto iter = mSequences.begin(),
1974 end = mSequences.end();
1975 // There must be at least one pointer
1976 if (iter != end) {
1977 // All pointers mut be non-null
1978 auto &pFirst = *iter++;
1979 if (pFirst) {
1980 // All sequences must have the same sample formats, and sample block
1981 // factory
1982 return
1983 std::all_of(iter, end, [&](decltype(pFirst) pSequence) {
1984 return pSequence &&
1985 pSequence->GetSampleFormats() == pFirst->GetSampleFormats() &&
1986 pSequence->GetFactory() == pFirst->GetFactory();
1987 }) &&
1988 // All cut lines are non-null, satisfy the invariants, and match width
1989 std::all_of(mCutLines.begin(), mCutLines.end(),
1990 [width](const WaveClipHolder &pCutLine) {
1991 if (!(pCutLine && pCutLine->NChannels() == width))
1992 return false;
1993 if (!pCutLine->StrongInvariant()) {
1994 pCutLine->AssertOrRepairStrongInvariant();
1995 return false;
1996 }
1997 return true;
1998 });
1999 }
2000 }
2001 return false;
2002}
2003
2005{
2006 if (!CheckInvariants())
2007 return false;
2008 const auto width = NChannels();
2009 auto iter = mSequences.begin(),
2010 end = mSequences.end();
2011 assert(iter != end); // because CheckInvariants is true
2012 auto &pFirst = *iter++;
2013 assert(pFirst); // likewise
2014 // All sequences must have the same lengths
2015 return all_of(iter, end, [&](decltype(pFirst) pSequence) {
2016 assert(pSequence); // likewise
2017 return pSequence->GetNumSamples() == pFirst->GetNumSamples();
2018 });
2019 return false;
2020}
2021
2023{
2024 if (!StrongInvariant()) {
2025 assert(false);
2027 assert(StrongInvariant());
2028 }
2029}
2030
2032 : mClip{ clip }
2033{
2035}
2036
2038{
2039 assert(mClip.StrongInvariant());
2040}
2041
2043 : clip{ clip }
2044 , mTrimLeft{ clip.mTrimLeft }
2045 , mTrimRight{ clip.mTrimRight }
2046{
2047 sequences.reserve(clip.mSequences.size());
2048 auto &factory = clip.GetFactory();
2049 for (auto &pSequence : clip.mSequences)
2050 sequences.push_back(
2052 std::make_unique<Sequence>(*pSequence, factory));
2053}
2054
2056{
2057 if (!committed) {
2058 clip.mSequences.swap(sequences);
2059 clip.mTrimLeft = mTrimLeft;
2060 clip.mTrimRight = mTrimRight;
2061 }
2062}
@ Internal
Indicates internal failure from Audacity.
static RegisteredToolbarFactory factory
Toolkit-neutral facade for basic user interface services.
PitchAndSpeedPreset
Definition: ClipInterface.h:40
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
EffectReverbSettings preset
Definition: Reverb.cpp:44
std::shared_ptr< SampleBlockFactory > SampleBlockFactoryPtr
Definition: SampleBlock.h:31
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
char * samplePtr
Definition: SampleFormat.h:57
const char * constSamplePtr
Definition: SampleFormat.h:58
An AudacityException with no visible message.
static constexpr auto TrimRight_attr
Definition: WaveClip.cpp:940
static constexpr auto RawAudioTempo_attr
Definition: WaveClip.cpp:943
static constexpr auto ClipStretchRatio_attr
Definition: WaveClip.cpp:944
static constexpr auto Name_attr
Definition: WaveClip.cpp:945
static constexpr auto CentShiftAttr
Definition: WaveClip.cpp:941
static constexpr auto Offset_attr
Definition: WaveClip.cpp:938
static constexpr auto TrimLeft_attr
Definition: WaveClip.cpp:939
static constexpr auto PitchAndSpeedPreset_attr
Definition: WaveClip.cpp:942
std::shared_ptr< WaveClip > WaveClipHolder
Definition: WaveClip.h:43
std::vector< WaveClipHolder > WaveClipHolders
Definition: WaveClip.h:45
std::vector< Attribute > AttributesList
Definition: XMLTagHandler.h:40
Abstraction of a progress dialog with well defined time-to-completion estimate.
Definition: BasicUI.h:157
virtual ProgressResult Poll(unsigned long long numerator, unsigned long long denominator, const TranslatableString &message={})=0
Update the bar and poll for clicks. Call only on the main thread.
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:72
void CollapseRegion(double t0, double t1, double sampleDur) noexcept
Definition: Envelope.cpp:378
An object that sends messages to an open-ended list of subscribed callbacks.
Definition: Observer.h:108
CallbackReturn Publish(const Message &message)
Send a message to connected callbacks.
Definition: Observer.h:207
A move-only handle representing a connection to a Publisher.
Definition: Observer.h:70
Interface to libsoxr.
Definition: Resample.h:27
std::pair< size_t, size_t > Process(double factor, const float *inBuffer, size_t inBufferLen, bool lastFlag, float *outBuffer, size_t outBufferLen)
Main processing function. Resamples from the input buffer to the output buffer.
Definition: Resample.cpp:88
Two sample formats, remembering format of original source and describing stored format.
Definition: SampleFormat.h:79
sampleFormat Stored() const
Definition: SampleFormat.h:91
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
Definition: Sequence.h:53
static const char * Sequence_tag
Definition: Sequence.h:56
A MessageBoxException that shows a given, unvarying string.
static constexpr auto MaxCents
static constexpr auto MinCents
static bool IsPassThroughMode(double stretchRatio)
Can be thrown when user cancels operations, as with a progress dialog. Delayed handler does nothing.
Definition: UserException.h:17
Fix consistency of cutlines and envelope after deleting from Sequences.
Definition: WaveClip.h:890
const BlockArray * GetSequenceBlockArray() const
Definition: WaveClip.cpp:145
sampleCount GetPlayEndSample() const
Real end time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:166
void WriteXML(XMLWriter &xmlFile) const
Definition: WaveClip.cpp:178
WaveClip & GetClip()
Definition: WaveClip.h:96
sampleCount GetVisibleSampleCount() const override
Definition: WaveClip.cpp:188
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow) const
Definition: WaveClip.cpp:151
double SamplesToTime(sampleCount s) const noexcept
Definition: WaveClip.cpp:100
double GetPlayEndTime() const override
Definition: WaveClip.cpp:203
double GetTrimRight() const
Definition: WaveClip.cpp:183
Envelope & GetEnvelope()
Definition: WaveClip.cpp:64
constSamplePtr GetAppendBuffer() const
Definition: WaveClip.cpp:135
void SetSamples(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat)
Definition: WaveClip.cpp:171
int GetRate() const override
Definition: WaveClip.cpp:193
~WaveClipChannel() override
double GetTrimLeft() const
Definition: WaveClip.cpp:110
double GetPlayStartTime() const override
Definition: WaveClip.cpp:198
bool HasPitchOrSpeed() const
Definition: WaveClip.cpp:105
bool Intersects(double t0, double t1) const
Definition: WaveClip.cpp:74
const Sequence & GetSequence() const
Definition: WaveClip.cpp:127
double GetStretchRatio() const override
Definition: WaveClip.cpp:218
double End() const
Definition: WaveClip.cpp:84
AudioSegmentSampleView GetSampleView(double t0, double t1, bool mayThrow) const
Request interval samples within [t0, t1). t0 and t1 are truncated to the interval start and end....
Definition: WaveClip.cpp:90
sampleCount TimeToSamples(double time) const override
Definition: WaveClip.cpp:213
sampleCount GetPlayStartSample() const
Real start time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:161
bool GetSamples(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow=true) const
Definition: WaveClip.cpp:115
const size_t miChannel
Definition: WaveClip.h:194
double GetPlayDuration() const
Definition: WaveClip.cpp:208
double Start() const
Definition: WaveClip.cpp:79
float GetRMS(double t0, double t1, bool mayThrow) const
Definition: WaveClip.cpp:156
size_t GetAppendBufferLen() const
Definition: WaveClip.cpp:140
bool WithinPlayRegion(double t) const
t ∈ [...)
Definition: WaveClip.cpp:95
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:238
bool WithinPlayRegion(double t) const
t ∈ [...)
Definition: WaveClip.cpp:1900
std::shared_ptr< SampleBlock > AppendLegacyNewBlock(constSamplePtr buffer, sampleFormat format, size_t len)
Definition: WaveClip.cpp:826
int GetCentShift() const override
Definition: WaveClip.cpp:634
PitchAndSpeedPreset mPitchAndSpeedPreset
Definition: WaveClip.h:949
std::optional< double > mRawAudioTempo
Definition: WaveClip.h:955
std::vector< std::unique_ptr< Sequence > > GetEmptySequenceCopies() const
Definition: WaveClip.cpp:719
bool SetCentShift(int cents)
Definition: WaveClip.cpp:1588
bool HasPitchOrSpeed() const
Definition: WaveClip.cpp:667
void ClearRight(double t)
Definition: WaveClip.cpp:1324
bool CoversEntirePlayRegion(double t0, double t1) const
t0 <= [ and ) <= t1, such that removing [t0, t1) from the track deletes this clip.
Definition: WaveClip.cpp:1927
double GetStretchRatio() const override
Definition: WaveClip.cpp:625
double GetSequenceStartTime() const noexcept
Definition: WaveClip.cpp:1863
bool CheckInvariants() const
Check weak invariant conditions on mSequences and mCutlines.
Definition: WaveClip.cpp:1970
Site< WaveClip, WaveClipListener, ClientData::DeepCopying > Attachments
Definition: WaveClip.h:251
void SetPlayStartTime(double time)
Definition: WaveClip.cpp:1758
size_t GetMaxBlockSize() const
Definition: WaveClip.cpp:706
virtual Observer::Subscription SubscribeToPitchAndSpeedPresetChange(std::function< void(PitchAndSpeedPreset)> cb) const override
Definition: WaveClip.cpp:649
void SetSequenceStartTime(double startTime)
Definition: WaveClip.cpp:1870
bool mIsPlaceholder
Definition: WaveClip.h:976
void SetEnvelope(std::unique_ptr< Envelope > p)
Definition: WaveClip.cpp:411
void CloseLock() noexcept
Should be called upon project close. Not balanced by unlocking calls.
Definition: WaveClip.cpp:1559
size_t CountBlocks() const
Definition: WaveClip.cpp:693
void SetName(const wxString &name)
Definition: WaveClip.cpp:1712
sampleCount TimeToSamples(double time) const override
Definition: WaveClip.cpp:1722
int mCentShift
Definition: WaveClip.h:950
bool RemoveCutLine(double cutLinePosition)
Remove cut line, without expanding the audio in it.
Definition: WaveClip.cpp:1533
sampleCount TimeToSequenceSamples(double t) const
Definition: WaveClip.cpp:1961
PitchAndSpeedPreset GetPitchAndSpeedPreset() const
Definition: WaveClip.cpp:1606
bool StretchRatioEquals(double value) const
Definition: WaveClip.cpp:672
double Start() const override
Definition: WaveClip.cpp:340
std::shared_ptr< ChannelInterval > DoGetChannel(size_t iChannel) override
Retrieve a channel.
Definition: WaveClip.cpp:350
void ShiftBy(double delta) noexcept
Definition: WaveClip.cpp:1889
std::unique_ptr< Envelope > mEnvelope
Envelope is unique, not per-sequence, and always non-null.
Definition: WaveClip.h:966
sampleCount GetVisibleSampleCount() const override
Definition: WaveClip.cpp:1795
double End() const override
Definition: WaveClip.cpp:345
double GetTrimRight() const noexcept
Returns the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:1816
double GetPlayStartTime() const noexcept override
Definition: WaveClip.cpp:1753
double mTrimRight
Definition: WaveClip.h:946
std::optional< double > mProjectTempo
Definition: WaveClip.h:956
bool AtOrBeforePlayRegion(double t) const
t <= [
Definition: WaveClip.cpp:1938
wxString mName
Definition: WaveClip.h:978
Observer::Subscription SubscribeToCentShiftChange(std::function< void(int)> cb) const override
Definition: WaveClip.cpp:640
int GetRate() const override
Definition: WaveClip.h:337
int mRate
Sample rate of the raw audio, i.e., before stretching.
Definition: WaveClip.h:959
double GetTrimLeft() const noexcept
Returns the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:1806
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
Definition: WaveClip.cpp:1034
ClearSequenceFinisher ClearSequence(double t0, double t1)
Definition: WaveClip.cpp:1337
void AppendLegacySharedBlock(const std::shared_ptr< SampleBlock > &pBlock)
Definition: WaveClip.cpp:835
sampleCount CountSamples(double t0, double t1) const
Definition: WaveClip.cpp:1948
sampleCount GetPlayEndSample() const
Real end time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:1790
constSamplePtr GetAppendBuffer(size_t ii) const
Get one channel of the append buffer.
Definition: WaveClip.cpp:729
double GetPlayEndTime() const override
Definition: WaveClip.cpp:1763
const wxString & GetName() const
Definition: WaveClip.cpp:1717
std::shared_ptr< SampleBlock > AppendToChannel(size_t iChannel, constSamplePtr buffer, sampleFormat format, size_t len)
Definition: WaveClip.cpp:817
void Resample(int rate, BasicUI::ProgressDialog *progress=nullptr)
Definition: WaveClip.cpp:1612
void ClearLeft(double t)
Definition: WaveClip.cpp:1310
void TrimLeftTo(double to)
Sets the the left trimming to the absolute time (if that is in bounds)
Definition: WaveClip.cpp:1850
sampleCount GetPlayStartSample() const
Real start time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:1785
void AppendSilence(double len, double envelopeValue)
Definition: WaveClip.cpp:1276
void SetRate(int rate)
Definition: WaveClip.cpp:1568
bool FindCutLine(double cutLinePosition, double *cutLineStart=nullptr, double *cutLineEnd=nullptr) const
Definition: WaveClip.cpp:1478
void WriteXML(size_t ii, XMLWriter &xmlFile) const
Definition: WaveClip.cpp:1066
void RepairChannels()
Ensure that all sequences have the same sample count.
Definition: WaveClip.cpp:918
SampleFormats GetSampleFormats() const
Definition: WaveClip.cpp:687
sampleCount GetSequenceStartSample() const
Returns the index of the first sample of the underlying sequence.
Definition: WaveClip.cpp:1884
void OnProjectTempoChange(const std::optional< double > &oldTempo, double newTempo)
Definition: WaveClip.cpp:545
virtual ~WaveClip()
Definition: WaveClip.cpp:335
bool GetIsPlaceholder() const
Definition: WaveClip.h:772
void HandleXMLEndTag(const std::string_view &tag) override
Definition: WaveClip.cpp:1020
double mSequenceOffset
Definition: WaveClip.h:944
void InsertSilence(double t, double len, double *pEnvelopeValue=nullptr)
Definition: WaveClip.cpp:1229
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
Definition: WaveClip.cpp:947
void TrimQuarternotesFromRight(double quarters)
Same as TrimRight, but expressed as quarter notes.
Definition: WaveClip.cpp:1831
void AssertOrRepairStrongInvariant()
Definition: WaveClip.cpp:2022
void SetPitchAndSpeedPreset(PitchAndSpeedPreset preset)
Definition: WaveClip.cpp:1599
void SetSilence(sampleCount offset, sampleCount length)
Silences the 'length' amount of samples starting from 'offset'(relative to the play start)
Definition: WaveClip.cpp:1737
bool BeforePlayRegion(double t) const
t < [
Definition: WaveClip.cpp:1933
void SetSamples(size_t ii, constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat)
Definition: WaveClip.cpp:397
Envelope & GetEnvelope() noexcept
Definition: WaveClip.h:553
void TrimRightTo(double to)
Sets the the right trimming to the absolute time (if that is in bounds)
Definition: WaveClip.cpp:1857
bool SplitsPlayRegion(double t) const
[ < t and t < ), such that if the track were split at t, it would split this clip in two of lengths >...
Definition: WaveClip.cpp:1895
bool GetSamples(size_t ii, samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow=true) const
Get samples from one channel.
Definition: WaveClip.cpp:378
float GetRMS(size_t ii, double t0, double t1, bool mayThrow) const
Definition: WaveClip.cpp:764
double SnapToTrackSample(double time) const noexcept
Definition: WaveClip.cpp:1732
void StretchRightTo(double to)
Sets from the right to the absolute time (if in expected range)
Definition: WaveClip.cpp:588
bool EntirelyWithinPlayRegion(double t0, double t1) const
t0 and t1 both ∈ [...)
Definition: WaveClip.cpp:1905
void OffsetCutLines(double t0, double len)
Offset cutlines right to time 't0' by time amount 'len'.
Definition: WaveClip.cpp:1550
void SetTrimRight(double trim)
Sets the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:1811
std::shared_ptr< WaveClip > SplitChannels()
Definition: WaveClip.cpp:480
std::pair< float, float > GetMinMax(size_t ii, double t0, double t1, bool mayThrow) const
Definition: WaveClip.cpp:740
void TrimRight(double deltaTime)
Moves play end position by deltaTime.
Definition: WaveClip.cpp:1826
bool AfterPlayRegion(double t) const
) <= t
Definition: WaveClip.cpp:1943
size_t GetAppendBufferLen(size_t ii) const
Definition: WaveClip.cpp:423
size_t GetBestBlockSize(sampleCount t) const
A hint for sizing of well aligned fetches.
Definition: WaveClip.cpp:701
double SamplesToTime(sampleCount s) const noexcept
Definition: WaveClip.cpp:1727
static void FixSplitCutlines(WaveClipHolders &myCutlines, WaveClipHolders &newCutlines)
Definition: WaveClip.cpp:462
const BlockArray * GetSequenceBlockArray(size_t ii) const
Definition: WaveClip.cpp:417
sampleCount GetSequenceSamplesCount() const
Definition: WaveClip.cpp:1748
void SetRawAudioTempo(double tempo)
Definition: WaveClip.cpp:1583
size_t GreatestAppendBufferLen() const
Definition: WaveClip.cpp:537
void ExpandCutLine(double cutLinePosition)
Definition: WaveClip.cpp:1499
void ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
Definition: WaveClip.cpp:782
const SampleBlockFactoryPtr & GetFactory() const
Definition: WaveClip.cpp:713
double GetPlayDuration() const
Definition: WaveClip.cpp:1775
size_t NChannels() const override
How many Sequences the clip contains.
Definition: WaveClip.cpp:373
WaveClip(const WaveClip &)=delete
bool IntersectsPlayRegion(double t0, double t1) const
[t0, t1) ∩ [...) != ∅
Definition: WaveClip.cpp:1919
AudioSegmentSampleView GetSampleView(size_t iChannel, sampleCount start, size_t length, bool mayThrow=true) const override
Request up to length samples. The actual number of samples available from the returned view is querie...
Definition: WaveClip.cpp:355
void ClearAndAddCutLine(double t0, double t1)
Definition: WaveClip.cpp:1406
bool IsEmpty() const
Definition: WaveClip.cpp:1780
void MarkChanged() noexcept
Called by mutating operations; notifies listeners.
Definition: WaveClip.cpp:735
bool HasEqualPitchAndSpeed(const WaveClip &other) const
Definition: WaveClip.cpp:661
sampleCount GetNumSamples() const
Definition: WaveClip.cpp:678
void AddCutLine(WaveClipHolder pClip)
Definition: WaveClip.cpp:1470
double mTrimLeft
Definition: WaveClip.h:945
void SwapChannels()
Definition: WaveClip.cpp:441
bool StrongInvariant() const
Definition: WaveClip.cpp:2004
WaveClipHolders mCutLines
Definition: WaveClip.h:973
void SetTrimLeft(double trim)
Sets the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:1801
double GetSequenceEndTime() const
Definition: WaveClip.cpp:1876
static const char * WaveClip_tag
Definition: WaveClip.h:249
Sequence * GetSequence(size_t ii)
Definition: WaveClip.h:571
void Clear(double t0, double t1)
Definition: WaveClip.cpp:1283
bool Paste(double t0, const WaveClip &other)
Definition: WaveClip.cpp:1100
void UpdateEnvelopeTrackLen()
Definition: WaveClip.cpp:807
size_t NumCutLines() const
Definition: WaveClip.h:731
void StretchCutLines(double ratioChange)
Definition: WaveClip.cpp:613
bool Append(size_t iChannel, size_t nChannels, constSamplePtr buffers[], sampleFormat format, size_t len, unsigned int stride, sampleFormat effectiveFormat)
Definition: WaveClip.cpp:844
static void TransferSequence(WaveClip &origClip, WaveClip &newClip)
Definition: WaveClip.cpp:453
void StretchLeftTo(double to)
Stretches from left to the absolute time (if in expected range)
Definition: WaveClip.cpp:569
bool PartlyWithinPlayRegion(double t0, double t1) const
t0 xor t1 ∈ [...)
Definition: WaveClip.cpp:1913
void StretchBy(double ratio)
Definition: WaveClip.cpp:599
void MakeStereo(WaveClip &&other, bool mustAlign)
Definition: WaveClip.cpp:512
void Flush()
Flush must be called after last Append.
Definition: WaveClip.cpp:893
std::vector< std::unique_ptr< Sequence > > mSequences
Definition: WaveClip.h:964
void TrimLeft(double deltaTime)
Moves play start position by deltaTime.
Definition: WaveClip.cpp:1821
void DiscardRightChannel()
Reduce width.
Definition: WaveClip.cpp:429
double mClipStretchRatio
Definition: WaveClip.h:954
A view into an attribute value. The class does not take the ownership of the data.
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:42
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:25
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
long long as_long_long() const
Definition: SampleCount.h:48
double as_double() const
Definition: SampleCount.h:46
WAVE_TRACK_API sampleCount GetSequenceSamplesCount(const WaveTrack &track)
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:628
static CommandContext::TargetFactory::SubstituteInUnique< InteractiveOutputTargets > scope
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
Definition: fast_float.h:2512
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40
const int newValue
Definition: WaveClip.h:203
const PitchAndSpeedPreset newValue
Definition: WaveClip.h:212
StrongInvariantScope(WaveClip &clip)
Definition: WaveClip.cpp:2031
Restores state when an update loop over mSequences fails midway.
Definition: WaveClip.h:930
std::vector< std::unique_ptr< Sequence > > sequences
Definition: WaveClip.h:936
Transaction(WaveClip &clip)
Definition: WaveClip.cpp:2042
virtual void MarkChanged() noexcept=0
virtual void MakeStereo(WaveClipListener &&other, bool aligned)
Definition: WaveClip.cpp:50
virtual void Erase(size_t index)
Definition: WaveClip.cpp:58
virtual void WriteXMLAttributes(XMLWriter &writer) const
Definition: WaveClip.cpp:40
virtual ~WaveClipListener()=0
virtual bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
Definition: WaveClip.cpp:44
virtual void SwapChannels()
Default implementation does nothing.
Definition: WaveClip.cpp:54
virtual void Invalidate()=0