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 is always configured to have single channel.
1626 //Create Resample instance per each channel in the clip
1627 std::vector<::Resample> resample;
1628 for (unsigned n = 0; n < mSequences.size(); ++n)
1629 resample.emplace_back(true, factor, factor);// constant rate resampling
1630
1631 const size_t bufsize = 65536;
1632 Floats inBuffer{ bufsize };
1633 Floats outBuffer{ bufsize };
1634 sampleCount pos = 0;
1635 bool error = false;
1636 int outGenerated = 0;
1637 const auto numSamples = GetNumSamples();
1638
1639 // These sequences are appended to below
1640 auto newSequences = GetEmptySequenceCopies();
1641
1647 while (pos < numSamples || outGenerated > 0) {
1648 const auto inLen = limitSampleBufferSize( bufsize, numSamples - pos );
1649
1650 bool isLast = ((pos + inLen) == numSamples);
1651
1652 auto ppNewSequence = newSequences.begin();
1653 std::optional<std::pair<size_t, size_t>> results{};
1654 size_t nSequence = 0;
1655 for (auto &pSequence : mSequences) {
1656 auto &pNewSequence = *ppNewSequence++;
1657 if (
1658 inLen > 0 &&
1659 !pSequence->Get(
1660 (samplePtr)inBuffer.get(), floatSample, pos, inLen, true))
1661 {
1662 error = true;
1663 break;
1664 }
1665
1666 // Expect the same results for all channels, or else fail
1667 auto newResults = resample[nSequence].Process(factor, inBuffer.get(), inLen,
1668 isLast, outBuffer.get(), bufsize);
1669 if (!results)
1670 results.emplace(newResults);
1671 else if (*results != newResults) {
1672 error = true;
1673 break;
1674 }
1675
1676 outGenerated = results->second;
1677 if (outGenerated < 0) {
1678 error = true;
1679 break;
1680 }
1681
1682 pNewSequence->Append((samplePtr)outBuffer.get(), floatSample,
1683 outGenerated, 1,
1684 widestSampleFormat /* computed samples need dither */
1685 );
1686 ++nSequence;
1687 }
1688 if (results)
1689 pos += results->first;
1690
1691 if (progress)
1692 {
1693 auto updateResult = progress->Poll(
1694 pos.as_long_long(),
1695 numSamples.as_long_long()
1696 );
1697 error = (updateResult != BasicUI::ProgressResult::Success);
1698 if (error)
1699 throw UserException{};
1700 }
1701 }
1702
1703 if (error)
1706 XO("Resampling failed."),
1707 XO("Warning"),
1708 "Error:_Resampling"
1709 };
1710 else
1711 {
1712 // Use No-fail-guarantee in these steps
1713 mSequences = move(newSequences);
1714 mRate = rate;
1715 Flush();
1716 Attachments::ForEach( std::mem_fn( &WaveClipListener::Invalidate ) );
1717 MarkChanged();
1718 }
1719}
1720
1721void WaveClip::SetName(const wxString& name)
1722{
1723 mName = name;
1724}
1725
1726const wxString& WaveClip::GetName() const
1727{
1728 return mName;
1729}
1730
1732{
1733 return sampleCount(floor(time * mRate / GetStretchRatio() + 0.5));
1734}
1735
1736double WaveClip::SamplesToTime(sampleCount s) const noexcept
1737{
1738 return s.as_double() * GetStretchRatio() / mRate;
1739}
1740
1741double WaveClip::SnapToTrackSample(double t) const noexcept
1742{
1743 return std::round(t * mRate) / mRate;
1744}
1745
1747{
1748 StrongInvariantScope scope{ *this };
1749 const auto start = TimeToSamples(mTrimLeft) + offset;
1750 Transaction transaction{ *this };
1751 for (auto &pSequence : mSequences)
1752 pSequence->SetSilence(start, length);
1753 transaction.Commit();
1754 MarkChanged();
1755}
1756
1758{
1759 return GetNumSamples() * NChannels();
1760}
1761
1762double WaveClip::GetPlayStartTime() const noexcept
1763{
1765}
1766
1768{
1770}
1771
1773{
1774 const auto numSamples = GetNumSamples();
1775 double maxLen = mSequenceOffset +
1776 ((numSamples + GreatestAppendBufferLen()).as_double()) *
1778 mTrimRight;
1779 // JS: calculated value is not the length;
1780 // it is a maximum value and can be negative; no clipping to 0
1781 return SnapToTrackSample(maxLen);
1782}
1783
1785{
1786 return GetPlayEndTime() - GetPlayStartTime();
1787}
1788
1790{
1791 return std::floor(GetPlayDuration() * mRate + 0.5) < 2.0;
1792}
1793
1795{
1796 return sampleCount { GetPlayStartTime() * mRate + 0.5 };
1797}
1798
1800{
1801 return sampleCount { GetPlayEndTime() * mRate + 0.5 };
1802}
1803
1805{
1806 return GetNumSamples()
1808}
1809
1810void WaveClip::SetTrimLeft(double trim)
1811{
1812 mTrimLeft = std::max(.0, trim);
1813}
1814
1815double WaveClip::GetTrimLeft() const noexcept
1816{
1817 return mTrimLeft;
1818}
1819
1820void WaveClip::SetTrimRight(double trim)
1821{
1822 mTrimRight = std::max(.0, trim);
1823}
1824
1825double WaveClip::GetTrimRight() const noexcept
1826{
1827 return mTrimRight;
1828}
1829
1830void WaveClip::TrimLeft(double deltaTime)
1831{
1832 SetTrimLeft(mTrimLeft + deltaTime);
1833}
1834
1835void WaveClip::TrimRight(double deltaTime)
1836{
1837 SetTrimRight(mTrimRight + deltaTime);
1838}
1839
1841{
1842 assert(mRawAudioTempo.has_value());
1843 if (!mRawAudioTempo.has_value())
1844 return;
1845 const auto secondsPerQuarter = 60 * GetStretchRatio() / *mRawAudioTempo;
1846 // MH https://github.com/audacity/audacity/issues/5878: Clip boundaries are
1847 // quantized to the sample period. Music durations aren't, though.
1848 // `quarters` was probably chosen such that the clip ends exactly at some
1849 // musical grid snapping point. However, if we right-trim by `quarters`,
1850 // the clip's play end time might be rounded up to the next sample period,
1851 // overlapping the next snapping point on the musical grid. We don't want
1852 // this, or it would disturb music producers who want to horizontally
1853 // duplicate loops.
1854 const auto quantizedTrim =
1855 std::ceil(quarters * secondsPerQuarter * GetRate()) / GetRate();
1856 TrimRight(quantizedTrim);
1857}
1858
1860{
1861 mTrimLeft =
1864}
1865
1867{
1868 const auto endTime = SnapToTrackSample(GetSequenceEndTime());
1869 mTrimRight = endTime - std::clamp(to, GetPlayStartTime(), endTime);
1870}
1871
1872double WaveClip::GetSequenceStartTime() const noexcept
1873{
1874 // JS: mSequenceOffset is the minimum value and it is returned; no clipping to 0
1875 // Do we need to `SnapToTrackSample` before returning?
1876 return mSequenceOffset;
1877}
1878
1880{
1881 mSequenceOffset = startTime;
1882 mEnvelope->SetOffset(startTime);
1883}
1884
1886{
1887 const auto numSamples = GetNumSamples();
1888 double maxLen = GetSequenceStartTime() +
1889 numSamples.as_double() * GetStretchRatio() / mRate;
1890 return maxLen;
1891}
1892
1894{
1896}
1897
1898void WaveClip::ShiftBy(double delta) noexcept
1899{
1901 MarkChanged();
1902}
1903
1904bool WaveClip::SplitsPlayRegion(double t) const
1905{
1906 return GetPlayStartTime() < t && t < GetPlayEndTime();
1907}
1908
1909bool WaveClip::WithinPlayRegion(double t) const
1910{
1911 return GetPlayStartTime() <= t && t < GetPlayEndTime();
1912}
1913
1914bool WaveClip::EntirelyWithinPlayRegion(double t0, double t1) const
1915{
1916 assert(t0 <= t1);
1917 // t1 is the open end of the interval, hence it's ok if it's equal to the
1918 // open end of the play region.
1919 return !BeforePlayRegion(t0) && t1 <= GetPlayEndTime();
1920}
1921
1922bool WaveClip::PartlyWithinPlayRegion(double t0, double t1) const
1923{
1924 assert(t0 <= t1);
1925 return WithinPlayRegion(t0) != WithinPlayRegion(t1);
1926}
1927
1928bool WaveClip::IntersectsPlayRegion(double t0, double t1) const
1929{
1930 assert(t0 <= t1);
1931 // t1 is the open end of the interval, so it must be excluded from the closed
1932 // begin of the play region.
1933 return t0 < GetPlayEndTime() && GetPlayStartTime() < t1;
1934}
1935
1936bool WaveClip::CoversEntirePlayRegion(double t0, double t1) const
1937{
1938 assert(t0 <= t1);
1939 return t0 <= GetPlayStartTime() && GetPlayEndTime() <= t1;
1940}
1941
1942bool WaveClip::BeforePlayRegion(double t) const
1943{
1944 return t < GetPlayStartTime();
1945}
1946
1948{
1949 return t <= GetPlayStartTime();
1950}
1951
1952bool WaveClip::AfterPlayRegion(double t) const
1953{
1954 return GetPlayEndTime() <= t;
1955}
1956
1957sampleCount WaveClip::CountSamples(double t0, double t1) const
1958{
1959 if(t0 < t1)
1960 {
1961 t0 = std::max(t0, GetPlayStartTime());
1962 t1 = std::min(t1, GetPlayEndTime());
1963 const auto s0 = TimeToSamples(t0 - GetPlayStartTime());
1964 const auto s1 = TimeToSamples(t1 - GetPlayStartTime());
1965 return s1 - s0;
1966 }
1967 return { 0 };
1968}
1969
1971{
1972 if (t < GetSequenceStartTime())
1973 return 0;
1974 else if (t > GetSequenceEndTime())
1975 return GetNumSamples();
1976 return TimeToSamples(t - GetSequenceStartTime());
1977}
1978
1980{
1981 const auto width = NChannels();
1982 auto iter = mSequences.begin(),
1983 end = mSequences.end();
1984 // There must be at least one pointer
1985 if (iter != end) {
1986 // All pointers mut be non-null
1987 auto &pFirst = *iter++;
1988 if (pFirst) {
1989 // All sequences must have the same sample formats, and sample block
1990 // factory
1991 return
1992 std::all_of(iter, end, [&](decltype(pFirst) pSequence) {
1993 return pSequence &&
1994 pSequence->GetSampleFormats() == pFirst->GetSampleFormats() &&
1995 pSequence->GetFactory() == pFirst->GetFactory();
1996 }) &&
1997 // All cut lines are non-null, satisfy the invariants, and match width
1998 std::all_of(mCutLines.begin(), mCutLines.end(),
1999 [width](const WaveClipHolder &pCutLine) {
2000 if (!(pCutLine && pCutLine->NChannels() == width))
2001 return false;
2002 if (!pCutLine->StrongInvariant()) {
2003 pCutLine->AssertOrRepairStrongInvariant();
2004 return false;
2005 }
2006 return true;
2007 });
2008 }
2009 }
2010 return false;
2011}
2012
2014{
2015 if (!CheckInvariants())
2016 return false;
2017 const auto width = NChannels();
2018 auto iter = mSequences.begin(),
2019 end = mSequences.end();
2020 assert(iter != end); // because CheckInvariants is true
2021 auto &pFirst = *iter++;
2022 assert(pFirst); // likewise
2023 // All sequences must have the same lengths
2024 return all_of(iter, end, [&](decltype(pFirst) pSequence) {
2025 assert(pSequence); // likewise
2026 return pSequence->GetNumSamples() == pFirst->GetNumSamples();
2027 });
2028}
2029
2031{
2032 if (!StrongInvariant()) {
2033 assert(false);
2035 assert(StrongInvariant());
2036 }
2037}
2038
2040 : mClip{ clip }
2041{
2043}
2044
2046{
2047 assert(mClip.StrongInvariant());
2048}
2049
2051 : clip{ clip }
2052 , mTrimLeft{ clip.mTrimLeft }
2053 , mTrimRight{ clip.mTrimRight }
2054{
2055 sequences.reserve(clip.mSequences.size());
2056 auto &factory = clip.GetFactory();
2057 for (auto &pSequence : clip.mSequences)
2058 sequences.push_back(
2060 std::make_unique<Sequence>(*pSequence, factory));
2061}
2062
2064{
2065 if (!committed) {
2066 clip.mSequences.swap(sequences);
2067 clip.mTrimLeft = mTrimLeft;
2068 clip.mTrimRight = mTrimRight;
2069 }
2070}
@ 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)
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.
ReverbSettings preset
Definition: ReverbBase.cpp:25
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
wxString name
Definition: TagsEditor.cpp:166
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:164
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:396
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
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:1909
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:1936
double GetStretchRatio() const override
Definition: WaveClip.cpp:625
double GetSequenceStartTime() const noexcept
Definition: WaveClip.cpp:1872
bool CheckInvariants() const
Check weak invariant conditions on mSequences and mCutlines.
Definition: WaveClip.cpp:1979
Site< WaveClip, WaveClipListener, ClientData::DeepCopying > Attachments
Definition: WaveClip.h:251
void SetPlayStartTime(double time)
Definition: WaveClip.cpp:1767
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:1879
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:1721
sampleCount TimeToSamples(double time) const override
Definition: WaveClip.cpp:1731
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:1970
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:1898
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:1804
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:1825
double GetPlayStartTime() const noexcept override
Definition: WaveClip.cpp:1762
double mTrimRight
Definition: WaveClip.h:946
std::optional< double > mProjectTempo
Definition: WaveClip.h:956
bool AtOrBeforePlayRegion(double t) const
t <= [
Definition: WaveClip.cpp:1947
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:1815
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:1957
sampleCount GetPlayEndSample() const
Real end time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:1799
constSamplePtr GetAppendBuffer(size_t ii) const
Get one channel of the append buffer.
Definition: WaveClip.cpp:729
double GetPlayEndTime() const override
Definition: WaveClip.cpp:1772
const wxString & GetName() const
Definition: WaveClip.cpp:1726
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:1859
sampleCount GetPlayStartSample() const
Real start time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:1794
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:1893
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:1840
void AssertOrRepairStrongInvariant()
Definition: WaveClip.cpp:2030
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:1746
bool BeforePlayRegion(double t) const
t < [
Definition: WaveClip.cpp:1942
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:1866
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:1904
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:1741
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:1914
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:1820
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:1835
bool AfterPlayRegion(double t) const
) <= t
Definition: WaveClip.cpp:1952
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:1736
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:1757
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:1784
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:1928
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:1789
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:2013
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:1810
double GetSequenceEndTime() const
Definition: WaveClip.cpp:1885
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:1922
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:1830
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:634
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:2039
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:2050
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