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
37{
38}
39
40WaveClip::WaveClip(size_t width,
42 sampleFormat format, int rate, int colourIndex)
43{
44 assert(width > 0);
45 mRate = rate;
46 mColourIndex = colourIndex;
47 mSequences.resize(width);
48 for (auto &pSequence : mSequences)
49 pSequence = std::make_unique<Sequence>(factory,
51
52 mEnvelope = std::make_unique<Envelope>(true, 1e-7, 2.0, 1.0);
53 assert(CheckInvariants());
54}
55
57 const WaveClip& orig, const SampleBlockFactoryPtr& factory,
58 bool copyCutlines)
59 : mClipStretchRatio { orig.mClipStretchRatio }
60 , mRawAudioTempo { orig.mRawAudioTempo }
61 , mProjectTempo { orig.mProjectTempo }
62{
63 // essentially a copy constructor - but you must pass in the
64 // current sample block factory, because we might be copying
65 // from one project to another
66
68 mTrimLeft = orig.mTrimLeft;
70 mRate = orig.mRate;
72 mSequences.reserve(orig.GetWidth());
73 for (auto &pSequence : orig.mSequences)
74 mSequences.push_back(
75 std::make_unique<Sequence>(*pSequence, factory));
76
77 mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
78
79 mName = orig.mName;
80
81 if (copyCutlines)
82 for (const auto &clip: orig.mCutLines)
83 mCutLines.push_back(std::make_shared<WaveClip>(*clip, factory, true));
84
86
87 assert(GetWidth() == orig.GetWidth());
88 assert(CheckInvariants());
89}
90
92 const WaveClip& orig, const SampleBlockFactoryPtr& factory,
93 bool copyCutlines, double t0, double t1)
94 : mClipStretchRatio { orig.mClipStretchRatio }
95 , mRawAudioTempo { orig.mRawAudioTempo }
96 , mProjectTempo { orig.mProjectTempo }
97{
98 assert(orig.CountSamples(t0, t1) > 0);
99
101
102 //Adjust trim values to sample-boundary
103 if(t0 > orig.GetPlayStartTime()) {
104 const auto s0 = orig.TimeToSamples(t0 - orig.GetSequenceStartTime());
105 mTrimLeft = orig.SamplesToTime(s0);
106
107 }
108 else
109 mTrimLeft = orig.mTrimLeft;
110
111 if(t1 < orig.GetPlayEndTime())
112 {
113 const auto s1 = orig.TimeToSamples(orig.GetSequenceEndTime() - t1);
114 mTrimRight = orig.SamplesToTime(s1);
115 }
116 else
117 mTrimRight = orig.mTrimRight;
118
119 mRate = orig.mRate;
121
123
124 mSequences.reserve(orig.GetWidth());
125 for (auto &pSequence : orig.mSequences)
126 mSequences.push_back(
127 std::make_unique<Sequence>(*pSequence, factory));
128
129 mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
130
131 if (copyCutlines)
132 for (const auto &cutline : orig.mCutLines)
133 mCutLines.push_back(
134 std::make_shared<WaveClip>(*cutline, factory, true));
135
136 assert(GetWidth() == orig.GetWidth());
137 assert(CheckInvariants());
138}
139
140
142{
143}
144
146 size_t ii, sampleCount start, size_t length, bool mayThrow) const
147{
148 assert(ii < GetWidth());
149 return mSequences[ii]->GetFloatSampleView(
150 start + TimeToSamples(mTrimLeft), length, mayThrow);
151}
152
154 size_t iChannel, double t0, double t1, bool mayThrow) const
155{
156 assert(iChannel < GetWidth());
157 const auto start = TimeToSamples(std::max(0., t0));
158 const auto length =
159 (std::min(GetNumSamples(), TimeToSamples(t1)) - start).as_size_t();
160 return GetSampleView(iChannel, start, length, mayThrow);
161}
162
163size_t WaveClip::GetWidth() const
164{
165 return mSequences.size();
166}
167
168bool WaveClip::GetSamples(size_t ii,
170 sampleCount start, size_t len, bool mayThrow) const
171{
172 assert(ii < GetWidth());
173 return mSequences[ii]
174 ->Get(buffer, format, start + TimeToSamples(mTrimLeft), len, mayThrow);
175}
176
178 sampleCount start, size_t len, bool mayThrow) const
179{
180 bool result = true;
181 for (size_t ii = 0, width = GetWidth(); result && ii < width; ++ii)
182 result = GetSamples(ii, buffers[ii], format, start, len, mayThrow);
183 return result;
184}
185
187void WaveClip::SetSamples(size_t ii,
189 sampleCount start, size_t len, sampleFormat effectiveFormat)
190{
191 assert(ii < GetWidth());
192 // use Strong-guarantee
193 mSequences[ii]->SetSamples(buffer, format,
194 start + TimeToSamples(mTrimLeft), len, effectiveFormat);
195
196 // use No-fail-guarantee
197 MarkChanged();
198}
199
201 double t, size_t iChannel, float& value, bool mayThrow) const
202{
203 if (!WithinPlayRegion(t))
204 return false;
205 const auto start = TimeToSamples(t);
206 return GetSamples(
207 iChannel, reinterpret_cast<samplePtr>(&value), floatSample, start, 1u,
208 mayThrow);
209}
210
212 double t, size_t iChannel, const float* buffer, size_t numFloats,
213 sampleFormat effectiveFormat)
214{
215 const auto maybeNegativeStart = TimeToSamples(t);
216 const auto maybeOutOfBoundEnd = maybeNegativeStart + numFloats;
217 const auto effectiveStart = std::max(sampleCount { 0 }, maybeNegativeStart);
218 const auto effectiveEnd =
219 std::min(GetVisibleSampleCount(), maybeOutOfBoundEnd);
220 if (effectiveStart >= effectiveEnd)
221 return;
222 // Cannot be greater than `numFloats` -> safe cast
223 const auto effectiveLen = (effectiveEnd - effectiveStart).as_size_t();
224 // Cannot be greater than `numFloats` -> safe cast
225 const auto numLeadingZeros =
226 (effectiveStart - maybeNegativeStart).as_size_t();
227 const auto offsetBuffer =
228 reinterpret_cast<const char*>(buffer + numLeadingZeros);
230 iChannel, offsetBuffer, floatSample, effectiveStart, effectiveLen,
231 effectiveFormat);
232}
233
235 double t, size_t iChannel, const float* buffer, size_t numSideSamples,
236 sampleFormat effectiveFormat)
237{
239 t - SamplesToTime(numSideSamples), iChannel, buffer,
240 2 * numSideSamples + 1, effectiveFormat);
241}
242
244 double t, size_t iChannel, float value, sampleFormat effectiveFormat)
245{
246 SetFloatsCenteredAroundTime(t, iChannel, &value, 0u, effectiveFormat);
247}
248
249void WaveClip::SetEnvelope(std::unique_ptr<Envelope> p)
250{
251 mEnvelope = move(p);
252}
253
255{
256 assert(ii < GetWidth());
257 return &mSequences[ii]->GetBlockArray();
258}
259
261{
262 assert(ii < GetWidth());
263 return &mSequences[ii]->GetBlockArray();
264}
265
267{
268 // All append buffers have equal lengths by class invariant
269 return mSequences[0]->GetAppendBufferLen();
270}
271
273 const std::optional<double>& oldTempo, double newTempo)
274{
275 if (!mRawAudioTempo.has_value())
276 // When we have tempo detection ready (either by header-file
277 // read-up or signal analysis) we can use something smarter than that. In
278 // the meantime, use the tempo of the project when the clip is created as
279 // source tempo.
280 mRawAudioTempo = oldTempo.value_or(newTempo);
281
282 if (oldTempo.has_value())
283 {
284 const auto ratioChange = *oldTempo / newTempo;
285 mSequenceOffset *= ratioChange;
286 mTrimLeft *= ratioChange;
287 mTrimRight *= ratioChange;
288 StretchCutLines(ratioChange);
289 mEnvelope->RescaleTimesBy(ratioChange);
290 }
291 mProjectTempo = newTempo;
292}
293
295{
296 const auto pet = GetPlayEndTime();
297 if (to >= pet)
298 return;
299 const auto oldPlayDuration = pet - GetPlayStartTime();
300 const auto newPlayDuration = pet - to;
301 const auto ratioChange = newPlayDuration / oldPlayDuration;
302 mSequenceOffset = pet - (pet - mSequenceOffset) * ratioChange;
303 mTrimLeft *= ratioChange;
304 mTrimRight *= ratioChange;
305 mClipStretchRatio *= ratioChange;
306 mEnvelope->SetOffset(mSequenceOffset);
307 mEnvelope->RescaleTimesBy(ratioChange);
308 StretchCutLines(ratioChange);
309}
310
312{
313 const auto pst = GetPlayStartTime();
314 if (to <= pst)
315 return;
316 const auto oldPlayDuration = GetPlayEndTime() - pst;
317 const auto newPlayDuration = to - pst;
318 const auto ratioChange = newPlayDuration / oldPlayDuration;
319 mSequenceOffset = pst - mTrimLeft * ratioChange;
320 mTrimLeft *= ratioChange;
321 mTrimRight *= ratioChange;
322 mClipStretchRatio *= ratioChange;
323 mEnvelope->SetOffset(mSequenceOffset);
324 mEnvelope->RescaleTimesBy(ratioChange);
325 StretchCutLines(ratioChange);
326}
327
328void WaveClip::StretchCutLines(double ratioChange)
329{
330 for (const auto& cutline : mCutLines)
331 {
332 cutline->mSequenceOffset *= ratioChange;
333 cutline->mTrimLeft *= ratioChange;
334 cutline->mTrimRight *= ratioChange;
335 cutline->mClipStretchRatio *= ratioChange;
336 cutline->mEnvelope->RescaleTimesBy(ratioChange);
337 }
338}
339
341{
342 const auto dstSrcRatio =
343 mProjectTempo.has_value() && mRawAudioTempo.has_value() ?
345 1.0;
346 return mClipStretchRatio * dstSrcRatio;
347}
348
350{
351 return StretchRatioEquals(other.GetStretchRatio());
352}
353
354bool WaveClip::StretchRatioEquals(double value) const
355{
357 1 + GetStretchRatio() - value);
358}
359
361{
362 // All sequences have equal lengths by class invariant
363 return mSequences[0]->GetNumSamples();
364}
365
367{
368 // All sequences have the same formats by class invariant
369 return mSequences[0]->GetSampleFormats();
370}
371
373{
374 // All sequences have the same factory by class invariant
375 return mSequences[0]->GetFactory();
376}
377
378std::vector<std::unique_ptr<Sequence>> WaveClip::GetEmptySequenceCopies() const
379{
380 decltype(mSequences) newSequences;
381 newSequences.reserve(mSequences.size());
382 for (auto& pSequence : mSequences)
383 newSequences.push_back(std::make_unique<Sequence>(
384 pSequence->GetFactory(), pSequence->GetSampleFormats()));
385 return newSequences;
386}
387
389{
390 assert(ii < GetWidth());
391 return mSequences[ii]->GetAppendBuffer();
392}
393
394void WaveClip::MarkChanged() // NOFAIL-GUARANTEE
395{
396 Caches::ForEach( std::mem_fn( &WaveClipListener::MarkChanged ) );
397}
398
399std::pair<float, float> WaveClip::GetMinMax(size_t ii,
400 double t0, double t1, bool mayThrow) const
401{
402 assert(ii < GetWidth());
403 t0 = std::max(t0, GetPlayStartTime());
404 t1 = std::min(t1, GetPlayEndTime());
405 if (t0 > t1) {
406 if (mayThrow)
408 return {
409 0.f, // harmless, but unused since Sequence::GetMinMax does not use these values
410 0.f // harmless, but unused since Sequence::GetMinMax does not use these values
411 };
412 }
413
414 if (t0 == t1)
415 return{ 0.f, 0.f };
416
417 auto s0 = TimeToSequenceSamples(t0);
418 auto s1 = TimeToSequenceSamples(t1);
419
420 return mSequences[ii]->GetMinMax(s0, s1 - s0, mayThrow);
421}
422
423float WaveClip::GetRMS(size_t ii, double t0, double t1, bool mayThrow) const
424{
425 assert(ii < GetWidth());
426 if (t0 > t1) {
427 if (mayThrow)
429 return 0.f;
430 }
431
432 if (t0 == t1)
433 return 0.f;
434
435 auto s0 = TimeToSequenceSamples(t0);
436 auto s1 = TimeToSequenceSamples(t1);
437
438 return mSequences[ii]->GetRMS(s0, s1-s0, mayThrow);
439}
440
442 const std::function<void(size_t)> & progressReport)
443{
444 // Note: it is not necessary to do this recursively to cutlines.
445 // They get converted as needed when they are expanded.
446
447 Transaction transaction{ *this };
448
449 auto bChanged = mSequences[0]->ConvertToSampleFormat(format, progressReport);
450 for (size_t ii = 1, width = GetWidth(); ii < width; ++ii) {
451 bool alsoChanged =
452 mSequences[ii]->ConvertToSampleFormat(format, progressReport);
453 // Class invariant implies:
454 assert(bChanged == alsoChanged);
455 }
456 if (bChanged)
457 MarkChanged();
458
459 transaction.Commit();
460}
461
464{
465 // The envelope time points account for stretching.
466 const auto len = GetNumSamples().as_double() * GetStretchRatio() / mRate;
467 if (len != mEnvelope->GetTrackLen())
468 mEnvelope->SetTrackLen(len, 1.0 / GetRate());
469}
470
472std::shared_ptr<SampleBlock>
474{
475 // This is a special use function for legacy files only and this assertion
476 // does not need to be relaxed
477 assert(GetWidth() == 1);
478 return mSequences[0]->AppendNewBlock( buffer, format, len );
479}
480
482void WaveClip::AppendSharedBlock(const std::shared_ptr<SampleBlock> &pBlock)
483{
484 // This is a special use function for legacy files only and this assertion
485 // does not need to be relaxed
486 assert(GetWidth() == 1);
487 mSequences[0]->AppendSharedBlock( pBlock );
488}
489
491 size_t len, unsigned int stride, sampleFormat effectiveFormat)
492{
493 Finally Do{ [this]{ assert(CheckInvariants()); } };
494
495 Transaction transaction{ *this };
496
497 //wxLogDebug(wxT("Append: len=%lli"), (long long) len);
498
499 size_t ii = 0;
500 bool appended = false;
501 for (auto &pSequence : mSequences)
502 appended =
503 pSequence->Append(buffers[ii++], format, len, stride, effectiveFormat)
504 || appended;
505
506 transaction.Commit();
507 // use No-fail-guarantee
509 MarkChanged();
510
511 return appended;
512}
513
515{
516 //wxLogDebug(wxT("WaveClip::Flush"));
517 //wxLogDebug(wxT(" mAppendBufferLen=%lli"), (long long) mAppendBufferLen);
518 //wxLogDebug(wxT(" previous sample count %lli"), (long long) mSequence->GetNumSamples());
519
520 if (GetAppendBufferLen() > 0) {
521
522 Transaction transaction{ *this };
523
524 for (auto &pSequence : mSequences)
525 pSequence->Flush();
526
527 transaction.Commit();
528
529 // No-fail operations
531 MarkChanged();
532 }
533
534 //wxLogDebug(wxT("now sample count %lli"), (long long) mSequence->GetNumSamples());
535}
536
537bool WaveClip::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
538{
539 if (tag == "waveclip")
540 {
541 double dblValue;
542 long longValue;
543 for (auto pair : attrs)
544 {
545 auto attr = pair.first;
546 auto value = pair.second;
547
548 if (attr == "offset")
549 {
550 if (!value.TryGet(dblValue))
551 return false;
552 SetSequenceStartTime(dblValue);
553 }
554 else if (attr == "trimLeft")
555 {
556 if (!value.TryGet(dblValue))
557 return false;
558 SetTrimLeft(dblValue);
559 }
560 else if (attr == "trimRight")
561 {
562 if (!value.TryGet(dblValue))
563 return false;
564 SetTrimRight(dblValue);
565 }
566 else if (attr == "rawAudioTempo")
567 {
568 if (!value.TryGet(dblValue))
569 return false;
570 if (dblValue == 0)
571 mRawAudioTempo.reset();
572 else
573 mRawAudioTempo = dblValue;
574 }
575 else if (attr == "clipStretchRatio")
576 {
577 if (!value.TryGet(dblValue))
578 return false;
579 mClipStretchRatio = dblValue;
580 }
581 else if (attr == "name")
582 {
583 if(value.IsStringView())
584 SetName(value.ToWString());
585 }
586 else if (attr == "colorindex")
587 {
588 if (!value.TryGet(longValue))
589 return false;
590 SetColourIndex(longValue);
591 }
592 }
593 return true;
594 }
595
596 return false;
597}
598
599void WaveClip::HandleXMLEndTag(const std::string_view& tag)
600{
601 // All blocks were deserialized into new sequences; remove the one made
602 // by the constructor which remains empty.
603 mSequences.erase(mSequences.begin());
604 mSequences.shrink_to_fit();
605 if (tag == "waveclip")
607 // A proof of this assertion assumes that nothing has happened since
608 // construction of this, besides calls to the other deserialization
609 // functions
610 assert(CheckInvariants());
611}
612
613XMLTagHandler *WaveClip::HandleXMLChild(const std::string_view& tag)
614{
615 auto &pFirst = mSequences[0];
616 if (tag == "sequence") {
617 mSequences.push_back(std::make_unique<Sequence>(
618 pFirst->GetFactory(), pFirst->GetSampleFormats()));
619 return mSequences.back().get();
620 }
621 else if (tag == "envelope")
622 return mEnvelope.get();
623 else if (tag == "waveclip")
624 {
625 // Nested wave clips are cut lines
626 auto format = pFirst->GetSampleFormats().Stored();
627 // The format is not stored in WaveClip itself but passed to
628 // Sequence::Sequence; but then the Sequence will deserialize format
629 // again
630 mCutLines.push_back(
631 std::make_shared<WaveClip>(
632 // Make only one channel now, but recursive deserialization
633 // increases the width later
634 1, pFirst->GetFactory(),
635 format, mRate, 0 /*colourindex*/));
636 return mCutLines.back().get();
637 }
638 else
639 return NULL;
640}
641
642void WaveClip::WriteXML(XMLWriter &xmlFile) const
643// may throw
644{
645 if (GetSequenceSamplesCount() <= 0)
646 // Oops, I'm empty? How did that happen? Anyway, I do nothing but causing
647 // problems, don't save me.
648 return;
649
650 xmlFile.StartTag(wxT("waveclip"));
651 xmlFile.WriteAttr(wxT("offset"), mSequenceOffset, 8);
652 xmlFile.WriteAttr(wxT("trimLeft"), mTrimLeft, 8);
653 xmlFile.WriteAttr(wxT("trimRight"), mTrimRight, 8);
654 xmlFile.WriteAttr(wxT("rawAudioTempo"), mRawAudioTempo.value_or(0.), 8);
655 xmlFile.WriteAttr(wxT("clipStretchRatio"), mClipStretchRatio, 8);
656 xmlFile.WriteAttr(wxT("name"), mName);
657 xmlFile.WriteAttr(wxT("colorindex"), mColourIndex );
658
659 for (auto &pSequence : mSequences)
660 pSequence->WriteXML(xmlFile);
661 mEnvelope->WriteXML(xmlFile);
662
663 for (const auto &clip: mCutLines)
664 clip->WriteXML(xmlFile);
665
666 xmlFile.EndTag(wxT("waveclip"));
667}
668
670bool WaveClip::Paste(double t0, const WaveClip& other)
671{
672 if (GetWidth() != other.GetWidth())
673 return false;
674
675 if (GetSequenceSamplesCount() == 0)
676 {
677 // Empty clip: we're flexible and adopt the other's stretching.
681 }
682 else if (GetStretchRatio() != other.GetStretchRatio())
683 return false;
684
685 Finally Do{ [this]{ assert(CheckInvariants()); } };
686
687 Transaction transaction{ *this };
688
689 const bool clipNeedsResampling = other.mRate != mRate;
690 const bool clipNeedsNewFormat =
692 std::shared_ptr<WaveClip> newClip;
693
694 t0 = std::clamp(t0, GetPlayStartTime(), GetPlayEndTime());
695
696 //seems like edge cases cannot happen, see WaveTrack::PasteWaveTrack
697 auto &factory = GetFactory();
698 if (t0 == GetPlayStartTime())
699 {
701 SetTrimLeft(other.GetTrimLeft());
702
703 auto copy = std::make_shared<WaveClip>(other, factory, true);
704 copy->ClearSequence(copy->GetPlayEndTime(), copy->GetSequenceEndTime());
705 newClip = std::move(copy);
706 }
707 else if (t0 == GetPlayEndTime())
708 {
710 SetTrimRight(other.GetTrimRight());
711
712 auto copy = std::make_shared<WaveClip>(other, factory, true);
713 copy->ClearSequence(copy->GetSequenceStartTime(), copy->GetPlayStartTime());
714 newClip = std::move(copy);
715 }
716 else
717 {
718 newClip = std::make_shared<WaveClip>(other, factory, true);
719 newClip->ClearSequence(newClip->GetPlayEndTime(), newClip->GetSequenceEndTime());
720 newClip->ClearSequence(newClip->GetSequenceStartTime(), newClip->GetPlayStartTime());
721 newClip->SetTrimLeft(0);
722 newClip->SetTrimRight(0);
723 }
724
725 if (clipNeedsResampling || clipNeedsNewFormat)
726 {
727 auto copy = std::make_shared<WaveClip>(*newClip.get(), factory, true);
728
729 if (clipNeedsResampling)
730 // The other clip's rate is different from ours, so resample
731 copy->Resample(mRate);
732
733 if (clipNeedsNewFormat)
734 // Force sample formats to match.
735 copy->ConvertToSampleFormat(GetSampleFormats().Stored());
736 newClip = std::move(copy);
737 }
738
739 // Paste cut lines contained in pasted clip
740 WaveClipHolders newCutlines;
741 for (const auto &cutline: newClip->mCutLines)
742 {
743 auto cutlineCopy = std::make_shared<WaveClip>(*cutline, factory,
744 // Recursively copy cutlines of cutlines. They don't need
745 // their offsets adjusted.
746 true);
747 cutlineCopy->ShiftBy(t0 - GetSequenceStartTime());
748 newCutlines.push_back(std::move(cutlineCopy));
749 }
750
752
753 // Because newClip was made above as a copy of (a copy of) other
754 assert(other.GetWidth() == newClip->GetWidth());
755 // And other has the same width as this, so this loop is safe
756 // Assume Strong-guarantee from Sequence::Paste
757 for (size_t ii = 0, width = GetWidth(); ii < width; ++ii)
758 mSequences[ii]->Paste(s0, newClip->mSequences[ii].get());
759
760 transaction.Commit();
761
762 // Assume No-fail-guarantee in the remaining
763 MarkChanged();
764 const auto sampleTime = 1.0 / GetRate();
765 const auto timeOffsetInEnvelope =
767 mEnvelope->PasteEnvelope(
768 timeOffsetInEnvelope, newClip->mEnvelope.get(), sampleTime);
769 OffsetCutLines(t0, newClip->GetPlayEndTime() - newClip->GetPlayStartTime());
770
771 for (auto &holder : newCutlines)
772 mCutLines.push_back(std::move(holder));
773
774 return true;
775}
776
778void WaveClip::InsertSilence( double t, double len, double *pEnvelopeValue )
779{
780 Transaction transaction{ *this };
781
782 if (t == GetPlayStartTime() && t > GetSequenceStartTime())
784 else if (t == GetPlayEndTime() && t < GetSequenceEndTime()) {
786 SetTrimRight(.0);
787 }
788
789 const auto s0 = TimeToSequenceSamples(t);
790 const auto slen = TimeToSamples(len);
791
792 // use Strong-guarantee
793 for (auto &pSequence : mSequences)
794 pSequence->InsertSilence(s0, slen);
795
796 transaction.Commit();
797
798 // use No-fail-guarantee
799 OffsetCutLines(t, len);
800
801 const auto sampleTime = 1.0 / GetRate();
802 auto pEnvelope = GetEnvelope();
803 if ( pEnvelopeValue ) {
804
805 // Preserve limit value at the end
806 auto oldLen = pEnvelope->GetTrackLen();
807 auto newLen = oldLen + len;
808 pEnvelope->Cap( sampleTime );
809
810 // Ramp across the silence to the given value
811 pEnvelope->SetTrackLen( newLen, sampleTime );
812 pEnvelope->InsertOrReplace
813 ( pEnvelope->GetOffset() + newLen, *pEnvelopeValue );
814 }
815 else
816 pEnvelope->InsertSpace( t, len );
817
818 MarkChanged();
819}
820
822void WaveClip::AppendSilence( double len, double envelopeValue )
823{
824 auto t = GetPlayEndTime();
825 InsertSilence( t, len, &envelopeValue );
826}
827
829void WaveClip::Clear(double t0, double t1)
830{
831 auto st0 = t0;
832 auto st1 = t1;
833 auto offset = .0;
834 if (st0 <= GetPlayStartTime())
835 {
836 offset = (t0 - GetPlayStartTime()) + GetTrimLeft();
837 st0 = GetSequenceStartTime();
838
839 SetTrimLeft(.0);
840 }
841 if (st1 >= GetPlayEndTime())
842 {
843 st1 = GetSequenceEndTime();
844 SetTrimRight(.0);
845 }
846 ClearSequence(st0, st1);
847
848 if (offset != .0)
849 ShiftBy(offset);
850}
851
853{
854 if (t > GetPlayStartTime() && t < GetPlayEndTime())
855 {
857 SetTrimLeft(.0);
859 }
860}
861
863{
864 if (t > GetPlayStartTime() && t < GetPlayEndTime())
865 {
867 SetTrimRight(.0);
868 }
869}
870
871void WaveClip::ClearSequence(double t0, double t1)
872{
873 Transaction transaction{ *this };
874
875 auto clip_t0 = std::max(t0, GetSequenceStartTime());
876 auto clip_t1 = std::min(t1, GetSequenceEndTime());
877
878 auto s0 = TimeToSequenceSamples(clip_t0);
879 auto s1 = TimeToSequenceSamples(clip_t1);
880
881 if (s0 != s1)
882 {
883 // use Strong-guarantee
884 for (auto &pSequence : mSequences)
885 pSequence->Delete(s0, s1 - s0);
886
887 // use No-fail-guarantee in the remaining
888
889 // msmeyer
890 //
891 // Delete all cutlines that are within the given area, if any.
892 //
893 // Note that when cutlines are active, two functions are used:
894 // Clear() and ClearAndAddCutLine(). ClearAndAddCutLine() is called
895 // whenever the user directly calls a command that removes some audio, e.g.
896 // "Cut" or "Clear" from the menu. This command takes care about recursive
897 // preserving of cutlines within clips. Clear() is called when internal
898 // operations want to remove audio. In the latter case, it is the right
899 // thing to just remove all cutlines within the area.
900 //
901
902 // May DELETE as we iterate, so don't use range-for
903 for (auto it = mCutLines.begin(); it != mCutLines.end();)
904 {
905 WaveClip* clip = it->get();
906 double cutlinePosition = GetSequenceStartTime() + clip->GetSequenceStartTime();
907 if (cutlinePosition >= t0 && cutlinePosition <= t1)
908 {
909 // This cutline is within the area, DELETE it
910 it = mCutLines.erase(it);
911 }
912 else
913 {
914 if (cutlinePosition >= t1)
915 {
916 clip->ShiftBy(clip_t0 - clip_t1);
917 }
918 ++it;
919 }
920 }
921
922 // Collapse envelope
923 auto sampleTime = 1.0 / GetRate();
924 GetEnvelope()->CollapseRegion(t0, t1, sampleTime);
925 }
926
927 transaction.Commit();
928 MarkChanged();
929}
930
934void WaveClip::ClearAndAddCutLine(double t0, double t1)
935{
936 if (t0 > GetPlayEndTime() || t1 < GetPlayStartTime() || CountSamples(t0, t1) == 0)
937 return; // no samples to remove
938
939 Transaction transaction{ *this };
940
941 const double clip_t0 = std::max( t0, GetPlayStartTime() );
942 const double clip_t1 = std::min( t1, GetPlayEndTime() );
943
944 auto newClip = std::make_shared<WaveClip>(
945 *this, GetFactory(), true, clip_t0, clip_t1);
946 if(t1 < GetPlayEndTime())
947 {
948 newClip->ClearSequence(t1, newClip->GetSequenceEndTime());
949 newClip->SetTrimRight(.0);
950 }
951 if(t0 > GetPlayStartTime())
952 {
953 newClip->ClearSequence(newClip->GetSequenceStartTime(), t0);
954 newClip->SetTrimLeft(.0);
955 }
956
957 newClip->SetSequenceStartTime( clip_t0 - GetSequenceStartTime() );
958
959 // Remove cutlines from this clip that were in the selection, shift
960 // left those that were after the selection
961 // May DELETE as we iterate, so don't use range-for
962 for (auto it = mCutLines.begin(); it != mCutLines.end();)
963 {
964 WaveClip* clip = it->get();
965 double cutlinePosition = GetSequenceStartTime() + clip->GetSequenceStartTime();
966 if (cutlinePosition >= t0 && cutlinePosition <= t1)
967 it = mCutLines.erase(it);
968 else
969 {
970 if (cutlinePosition >= t1)
971 {
972 clip->ShiftBy(clip_t0 - clip_t1);
973 }
974 ++it;
975 }
976 }
977
978 // Clear actual audio data
979 auto s0 = TimeToSequenceSamples(t0);
980 auto s1 = TimeToSequenceSamples(t1);
981
982 // use Weak-guarantee
983 for (auto &pSequence : mSequences)
984 pSequence->Delete(s0, s1-s0);
985
986 // Collapse envelope
987 auto sampleTime = 1.0 / GetRate();
988 GetEnvelope()->CollapseRegion( t0, t1, sampleTime );
989
990 transaction.Commit();
991 MarkChanged();
992
993 mCutLines.push_back(std::move(newClip));
994
995 // New cutline was copied from this so will have correct width
996 assert(CheckInvariants());
997}
998
999bool WaveClip::FindCutLine(double cutLinePosition,
1000 double* cutlineStart /* = NULL */,
1001 double* cutlineEnd /* = NULL */) const
1002{
1003 for (const auto &cutline: mCutLines)
1004 {
1005 if (fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001)
1006 {
1007 auto startTime = GetSequenceStartTime() + cutline->GetSequenceStartTime();
1008 if (cutlineStart)
1009 *cutlineStart = startTime;
1010 if (cutlineEnd)
1011 *cutlineEnd = startTime + cutline->SamplesToTime(cutline->GetVisibleSampleCount());
1012 return true;
1013 }
1014 }
1015
1016 return false;
1017}
1018
1020void WaveClip::ExpandCutLine(double cutLinePosition)
1021{
1022 auto end = mCutLines.end();
1023 auto it = std::find_if( mCutLines.begin(), end,
1024 [&](const WaveClipHolder &cutline) {
1025 return fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001;
1026 } );
1027
1028 if ( it != end ) {
1029 auto *cutline = it->get();
1030 // assume Strong-guarantee from Paste
1031
1032 // Envelope::Paste takes offset into account, WaveClip::Paste doesn't!
1033 // Do this to get the right result:
1034 cutline->mEnvelope->SetOffset(0);
1035 bool success = Paste(
1036 GetSequenceStartTime() + cutline->GetSequenceStartTime(), *cutline);
1037 assert(success); // class invariant promises cutlines have correct width
1038
1039 // Now erase the cutline,
1040 // but be careful to find it again, because Paste above may
1041 // have modified the array of cutlines (if our cutline contained
1042 // another cutline!), invalidating the iterator we had.
1043 end = mCutLines.end();
1044 it = std::find_if(mCutLines.begin(), end,
1045 [=](const WaveClipHolder &p) { return p.get() == cutline; });
1046 if (it != end)
1047 mCutLines.erase(it); // deletes cutline!
1048 else {
1049 wxASSERT(false);
1050 }
1051 }
1052}
1053
1054bool WaveClip::RemoveCutLine(double cutLinePosition)
1055{
1056 for (auto it = mCutLines.begin(); it != mCutLines.end(); ++it)
1057 {
1058 const auto &cutline = *it;
1059 //std::numeric_limits<double>::epsilon() or (1.0 / static_cast<double>(mRate))?
1060 if (fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001)
1061 {
1062 mCutLines.erase(it); // deletes cutline!
1063 return true;
1064 }
1065 }
1066
1067 return false;
1068}
1069
1071void WaveClip::OffsetCutLines(double t0, double len)
1072{
1073 for (const auto &cutLine : mCutLines)
1074 {
1075 if (GetSequenceStartTime() + cutLine->GetSequenceStartTime() >= t0)
1076 cutLine->ShiftBy(len);
1077 }
1078}
1079
1080void WaveClip::CloseLock() noexcept
1081{
1082 // Don't need a Transaction for noexcept operations
1083 for (auto &pSequence : mSequences)
1084 pSequence->CloseLock();
1085 for (const auto &cutline: mCutLines)
1086 cutline->CloseLock();
1087}
1088
1089void WaveClip::SetRate(int rate)
1090{
1091 const auto trimLeftSampleNum = TimeToSamples(mTrimLeft);
1092 const auto trimRightSampleNum = TimeToSamples(mTrimRight);
1093 auto ratio = static_cast<double>(mRate) / rate;
1094 mRate = rate;
1095 mTrimLeft = SamplesToTime(trimLeftSampleNum);
1096 mTrimRight = SamplesToTime(trimRightSampleNum);
1097 const auto newLength =
1099 mEnvelope->RescaleTimes(newLength);
1100 MarkChanged();
1102}
1103
1106{
1107 // Note: it is not necessary to do this recursively to cutlines.
1108 // They get resampled as needed when they are expanded.
1109
1110 if (rate == mRate)
1111 return; // Nothing to do
1112
1113 // This function does its own RAII without a Transaction
1114
1115 double factor = (double)rate / (double)mRate;
1116 ::Resample resample(true, factor, factor); // constant rate resampling
1117
1118 const size_t bufsize = 65536;
1119 Floats inBuffer{ bufsize };
1120 Floats outBuffer{ bufsize };
1121 sampleCount pos = 0;
1122 bool error = false;
1123 int outGenerated = 0;
1124 const auto numSamples = GetNumSamples();
1125
1126 // These sequences are appended to below
1127 auto newSequences = GetEmptySequenceCopies();
1128
1134 while (pos < numSamples || outGenerated > 0) {
1135 const auto inLen = limitSampleBufferSize( bufsize, numSamples - pos );
1136
1137 bool isLast = ((pos + inLen) == numSamples);
1138
1139 auto ppNewSequence = newSequences.begin();
1140 std::optional<std::pair<size_t, size_t>> results{};
1141 for (auto &pSequence : mSequences) {
1142 auto &pNewSequence = *ppNewSequence++;
1143 if (!pSequence->Get((samplePtr)inBuffer.get(), floatSample, pos, inLen, true))
1144 {
1145 error = true;
1146 break;
1147 }
1148
1149 // Expect the same results for all channels, or else fail
1150 auto newResults = resample.Process(factor, inBuffer.get(), inLen,
1151 isLast, outBuffer.get(), bufsize);
1152 if (!results)
1153 results.emplace(newResults);
1154 else if (*results != newResults) {
1155 error = true;
1156 break;
1157 }
1158
1159 outGenerated = results->second;
1160 if (outGenerated < 0) {
1161 error = true;
1162 break;
1163 }
1164
1165 pNewSequence->Append((samplePtr)outBuffer.get(), floatSample,
1166 outGenerated, 1,
1167 widestSampleFormat /* computed samples need dither */
1168 );
1169 }
1170 if (results)
1171 pos += results->first;
1172
1173 if (progress)
1174 {
1175 auto updateResult = progress->Poll(
1176 pos.as_long_long(),
1177 numSamples.as_long_long()
1178 );
1179 error = (updateResult != BasicUI::ProgressResult::Success);
1180 if (error)
1181 throw UserException{};
1182 }
1183 }
1184
1185 if (error)
1188 XO("Resampling failed."),
1189 XO("Warning"),
1190 "Error:_Resampling"
1191 };
1192 else
1193 {
1194 // Use No-fail-guarantee in these steps
1195 mSequences = move(newSequences);
1196 mRate = rate;
1197 Flush();
1198 Caches::ForEach( std::mem_fn( &WaveClipListener::Invalidate ) );
1199 }
1200}
1201
1202// Used by commands which interact with clips using the keyboard.
1203// When two clips are immediately next to each other, the GetPlayEndTime()
1204// of the first clip and the GetPlayStartTime() of the second clip may not
1205// be exactly equal due to rounding errors.
1207{
1208 double endThis = GetRate() * GetPlayStartTime() +
1210 double startNext = next->GetRate() * next->GetPlayStartTime();
1211
1212 // given that a double has about 15 significant digits, using a criterion
1213 // of half a sample should be safe in all normal usage.
1214 return fabs(startNext - endThis) < 0.5;
1215}
1216
1217void WaveClip::SetName(const wxString& name)
1218{
1219 mName = name;
1220}
1221
1222const wxString& WaveClip::GetName() const
1223{
1224 return mName;
1225}
1226
1228{
1229 return sampleCount(floor(time * mRate / GetStretchRatio() + 0.5));
1230}
1231
1232double WaveClip::SamplesToTime(sampleCount s) const noexcept
1233{
1234 return s.as_double() * GetStretchRatio() / mRate;
1235}
1236
1237double WaveClip::SnapToTrackSample(double t) const noexcept
1238{
1239 return std::round(t * mRate) / mRate;
1240}
1241
1243{
1244 const auto start = TimeToSamples(mTrimLeft) + offset;
1245 Transaction transaction{ *this };
1246 for (auto &pSequence : mSequences)
1247 pSequence->SetSilence(start, length);
1248 transaction.Commit();
1249 MarkChanged();
1250}
1251
1253{
1254 return GetNumSamples() * GetWidth();
1255}
1256
1257double WaveClip::GetPlayStartTime() const noexcept
1258{
1260}
1261
1263{
1265}
1266
1268{
1269 const auto numSamples = GetNumSamples();
1270 double maxLen = mSequenceOffset +
1271 ((numSamples + GetAppendBufferLen()).as_double()) *
1273 mTrimRight;
1274 // JS: calculated value is not the length;
1275 // it is a maximum value and can be negative; no clipping to 0
1276 return SnapToTrackSample(maxLen);
1277}
1278
1280{
1281 return GetPlayEndTime() - GetPlayStartTime();
1282}
1283
1285{
1286 return std::floor(GetPlayDuration() * mRate + 0.5) < 2.0;
1287}
1288
1290{
1291 return sampleCount { GetPlayStartTime() * mRate + 0.5 };
1292}
1293
1295{
1296 return sampleCount { GetPlayEndTime() * mRate + 0.5 };
1297}
1298
1300{
1301 return GetNumSamples()
1303}
1304
1305void WaveClip::SetTrimLeft(double trim)
1306{
1307 mTrimLeft = std::max(.0, trim);
1308}
1309
1310double WaveClip::GetTrimLeft() const noexcept
1311{
1312 return mTrimLeft;
1313}
1314
1315void WaveClip::SetTrimRight(double trim)
1316{
1317 mTrimRight = std::max(.0, trim);
1318}
1319
1320double WaveClip::GetTrimRight() const noexcept
1321{
1322 return mTrimRight;
1323}
1324
1325void WaveClip::TrimLeft(double deltaTime)
1326{
1327 SetTrimLeft(mTrimLeft + deltaTime);
1328}
1329
1330void WaveClip::TrimRight(double deltaTime)
1331{
1332 SetTrimRight(mTrimRight + deltaTime);
1333}
1334
1336{
1337 mTrimLeft =
1340}
1341
1343{
1344 const auto endTime = SnapToTrackSample(GetSequenceEndTime());
1345 mTrimRight = endTime - std::clamp(to, GetPlayStartTime(), endTime);
1346}
1347
1348double WaveClip::GetSequenceStartTime() const noexcept
1349{
1350 // JS: mSequenceOffset is the minimum value and it is returned; no clipping to 0
1351 // Do we need to `SnapToTrackSample` before returning?
1352 return mSequenceOffset;
1353}
1354
1356{
1357 mSequenceOffset = startTime;
1358 mEnvelope->SetOffset(startTime);
1359}
1360
1362{
1363 const auto numSamples = GetNumSamples();
1364 double maxLen = GetSequenceStartTime() +
1365 numSamples.as_double() * GetStretchRatio() / mRate;
1366 return maxLen;
1367}
1368
1370{
1372}
1373
1374void WaveClip::ShiftBy(double delta) noexcept
1375{
1376 SetSequenceStartTime(GetSequenceStartTime() + delta);
1377}
1378
1379bool WaveClip::SplitsPlayRegion(double t) const
1380{
1381 return GetPlayStartTime() < t && t < GetPlayEndTime();
1382}
1383
1384bool WaveClip::WithinPlayRegion(double t) const
1385{
1386 return GetPlayStartTime() <= t && t < GetPlayEndTime();
1387}
1388
1389bool WaveClip::EntirelyWithinPlayRegion(double t0, double t1) const
1390{
1391 assert(t0 <= t1);
1392 // t1 is the open end of the interval, hence it's ok if it's equal to the
1393 // open end of the play region.
1394 return !BeforePlayRegion(t0) && t1 <= GetPlayEndTime();
1395}
1396
1397bool WaveClip::PartlyWithinPlayRegion(double t0, double t1) const
1398{
1399 assert(t0 <= t1);
1400 return WithinPlayRegion(t0) != WithinPlayRegion(t1);
1401}
1402
1403bool WaveClip::IntersectsPlayRegion(double t0, double t1) const
1404{
1405 assert(t0 <= t1);
1406 // t1 is the open end of the interval, so it must be excluded from the closed
1407 // begin of the play region.
1408 return t0 < GetPlayEndTime() && GetPlayStartTime() < t1;
1409}
1410
1411bool WaveClip::CoversEntirePlayRegion(double t0, double t1) const
1412{
1413 assert(t0 <= t1);
1414 return t0 <= GetPlayStartTime() && GetPlayEndTime() <= t1;
1415}
1416
1417bool WaveClip::BeforePlayRegion(double t) const
1418{
1419 return t < GetPlayStartTime();
1420}
1421
1423{
1424 return t <= GetPlayStartTime();
1425}
1426
1427bool WaveClip::AfterPlayRegion(double t) const
1428{
1429 return GetPlayEndTime() <= t;
1430}
1431
1432sampleCount WaveClip::CountSamples(double t0, double t1) const
1433{
1434 if(t0 < t1)
1435 {
1436 t0 = std::max(t0, GetPlayStartTime());
1437 t1 = std::min(t1, GetPlayEndTime());
1438 const auto s0 = TimeToSamples(t0 - GetPlayStartTime());
1439 const auto s1 = TimeToSamples(t1 - GetPlayStartTime());
1440 return s1 - s0;
1441 }
1442 return { 0 };
1443}
1444
1446{
1447 if (t < GetSequenceStartTime())
1448 return 0;
1449 else if (t > GetSequenceEndTime())
1450 return GetNumSamples();
1451 return TimeToSamples(t - GetSequenceStartTime());
1452}
1453
1455{
1456 const auto width = GetWidth();
1457 auto iter = mSequences.begin(),
1458 end = mSequences.end();
1459 // There must be at least one pointer
1460 if (iter != end) {
1461 // All pointers mut be non-null
1462 auto &pFirst = *iter++;
1463 if (pFirst) {
1464 // All sequences must have the same lengths, append buffer lengths,
1465 // sample formats, and sample block factory
1466 return
1467 std::all_of(iter, end, [&](decltype(pFirst) pSequence) {
1468 return pSequence &&
1469 pSequence->GetNumSamples() == pFirst->GetNumSamples() &&
1470 pSequence->GetAppendBufferLen() == pFirst->GetAppendBufferLen()
1471 &&
1472 pSequence->GetSampleFormats() == pFirst->GetSampleFormats() &&
1473 pSequence->GetFactory() == pFirst->GetFactory();
1474 }) &&
1475 // All cut lines are non-null, satisfy the invariants, and match width
1476 std::all_of(mCutLines.begin(), mCutLines.end(),
1477 [width](const WaveClipHolder &pCutLine) {
1478 return pCutLine && pCutLine->GetWidth() == width &&
1479 pCutLine->CheckInvariants();
1480 });
1481 }
1482 }
1483 return false;
1484}
1485
1487 : clip{ clip }
1488 , mTrimLeft{ clip.mTrimLeft }
1489 , mTrimRight{ clip.mTrimRight }
1490{
1491 sequences.reserve(clip.mSequences.size());
1492 auto &factory = clip.GetFactory();
1493 for (auto &pSequence : clip.mSequences)
1494 sequences.push_back(
1496 std::make_unique<Sequence>(*pSequence, factory));
1497}
1498
1500{
1501 if (!committed) {
1502 clip.mSequences.swap(sequences);
1503 clip.mTrimLeft = mTrimLeft;
1504 clip.mTrimRight = mTrimRight;
1505 }
1506}
wxT("CloseDown"))
@ Internal
Indicates internal failure from Audacity.
Toolkit-neutral facade for basic user interface services.
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.
std::shared_ptr< SampleBlockFactory > SampleBlockFactoryPtr
Definition: SampleBlock.h:30
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.
std::shared_ptr< WaveClip > WaveClipHolder
Definition: WaveClip.h:45
std::vector< WaveClipHolder > WaveClipHolders
Definition: WaveClip.h:46
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.
void CollapseRegion(double t0, double t1, double sampleDur)
Definition: Envelope.cpp:378
Interface to libsoxr.
Definition: Resample.h:27
std::pair< size_t, size_t > Process(double factor, 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 MessageBoxException that shows a given, unvarying string.
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
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:103
bool WithinPlayRegion(double t) const
t ∈ [...)
Definition: WaveClip.cpp:1384
std::optional< double > mRawAudioTempo
Definition: WaveClip.h:613
std::vector< std::unique_ptr< Sequence > > GetEmptySequenceCopies() const
Definition: WaveClip.cpp:378
bool Append(constSamplePtr buffers[], sampleFormat format, size_t len, unsigned int stride, sampleFormat effectiveFormat)
Definition: WaveClip.cpp:490
void ClearRight(double t)
Definition: WaveClip.cpp:862
bool CoversEntirePlayRegion(double t0, double t1) const
t0 <= [ and ) <= t1, such that removing [t0, t1) from the track deletes this clip.
Definition: WaveClip.cpp:1411
double GetStretchRatio() const override
Definition: WaveClip.cpp:340
double GetSequenceStartTime() const noexcept
Definition: WaveClip.cpp:1348
bool CheckInvariants() const
Check invariant conditions on mSequences and mCutlines.
Definition: WaveClip.cpp:1454
void SetPlayStartTime(double time)
Definition: WaveClip.cpp:1262
void SetSequenceStartTime(double startTime)
Definition: WaveClip.cpp:1355
bool mIsPlaceholder
Definition: WaveClip.h:639
void SetEnvelope(std::unique_ptr< Envelope > p)
Definition: WaveClip.cpp:249
void CloseLock() noexcept
Should be called upon project close. Not balanced by unlocking calls.
Definition: WaveClip.cpp:1080
void SetName(const wxString &name)
Definition: WaveClip.cpp:1217
void MarkChanged()
Definition: WaveClip.cpp:394
sampleCount TimeToSamples(double time) const override
Definition: WaveClip.cpp:1227
bool RemoveCutLine(double cutLinePosition)
Remove cut line, without expanding the audio in it.
Definition: WaveClip.cpp:1054
sampleCount TimeToSequenceSamples(double t) const
Definition: WaveClip.cpp:1445
bool StretchRatioEquals(double value) const
Definition: WaveClip.cpp:354
void ShiftBy(double delta) noexcept
Definition: WaveClip.cpp:1374
std::unique_ptr< Envelope > mEnvelope
Envelope is unique, not per-sequence.
Definition: WaveClip.h:629
sampleCount GetVisibleSampleCount() const override
Definition: WaveClip.cpp:1299
double GetTrimRight() const noexcept
Returns the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:1320
double GetPlayStartTime() const noexcept override
Definition: WaveClip.cpp:1257
double mTrimRight
Definition: WaveClip.h:607
std::optional< double > mProjectTempo
Definition: WaveClip.h:614
bool AtOrBeforePlayRegion(double t) const
t <= [
Definition: WaveClip.cpp:1422
wxString mName
Definition: WaveClip.h:642
int GetRate() const override
Definition: WaveClip.h:156
int mRate
Sample rate of the raw audio, i.e., before stretching.
Definition: WaveClip.h:617
double GetTrimLeft() const noexcept
Returns the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:1310
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
Definition: WaveClip.cpp:613
sampleCount CountSamples(double t0, double t1) const
Definition: WaveClip.cpp:1432
sampleCount GetPlayEndSample() const
Real end time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:1294
constSamplePtr GetAppendBuffer(size_t ii) const
Get one channel of the append buffer.
Definition: WaveClip.cpp:388
double GetPlayEndTime() const override
Definition: WaveClip.cpp:1267
void SetFloatsFromTime(double t, size_t iChannel, const float *buffer, size_t numSamples, sampleFormat effectiveFormat)
Considers buffer as audio starting at TimeToSamples(t) (relative to clip play start time) and with eq...
Definition: WaveClip.cpp:211
const wxString & GetName() const
Definition: WaveClip.cpp:1222
void Resample(int rate, BasicUI::ProgressDialog *progress=nullptr)
Definition: WaveClip.cpp:1105
void ClearLeft(double t)
Definition: WaveClip.cpp:852
std::shared_ptr< SampleBlock > AppendNewBlock(constSamplePtr buffer, sampleFormat format, size_t len)
Definition: WaveClip.cpp:473
void TrimLeftTo(double to)
Sets the the left trimming to the absolute time (if that is in bounds)
Definition: WaveClip.cpp:1335
bool FindCutLine(double cutLinePosition, double *cutLineStart=NULL, double *cutLineEnd=NULL) const
Definition: WaveClip.cpp:999
sampleCount GetPlayStartSample() const
Real start time of the clip, quantized to raw sample rate (track's rate)
Definition: WaveClip.cpp:1289
void AppendSilence(double len, double envelopeValue)
Definition: WaveClip.cpp:822
void SetRate(int rate)
Definition: WaveClip.cpp:1089
SampleFormats GetSampleFormats() const
Definition: WaveClip.cpp:366
sampleCount GetSequenceStartSample() const
Returns the index of the first sample of the underlying sequence.
Definition: WaveClip.cpp:1369
void OnProjectTempoChange(const std::optional< double > &oldTempo, double newTempo)
Definition: WaveClip.cpp:272
virtual ~WaveClip()
Definition: WaveClip.cpp:141
bool GetIsPlaceholder() const
Definition: WaveClip.h:546
int mColourIndex
Definition: WaveClip.h:618
void HandleXMLEndTag(const std::string_view &tag) override
Definition: WaveClip.cpp:599
double mSequenceOffset
Definition: WaveClip.h:605
void InsertSilence(double t, double len, double *pEnvelopeValue=nullptr)
Definition: WaveClip.cpp:778
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
Definition: WaveClip.cpp:537
void SetColourIndex(int index)
Definition: WaveClip.h:181
BlockArray * GetSequenceBlockArray(size_t ii)
Definition: WaveClip.cpp:254
void SetSilence(sampleCount offset, sampleCount length)
Silences the 'length' amount of samples starting from 'offset'(relative to the play start)
Definition: WaveClip.cpp:1242
bool BeforePlayRegion(double t) const
t < [
Definition: WaveClip.cpp:1417
void SetSamples(size_t ii, constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat)
Definition: WaveClip.cpp:187
void TrimRightTo(double to)
Sets the the right trimming to the absolute time (if that is in bounds)
Definition: WaveClip.cpp:1342
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:1379
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:168
bool SharesBoundaryWithNextClip(const WaveClip *next) const
Definition: WaveClip.cpp:1206
void SetFloatsCenteredAroundTime(double t, size_t iChannel, const float *buffer, size_t numSideSamples, sampleFormat effectiveFormat)
Same as SetFloatsFromTime, but with buffer starting at TimeToSamples(t0 - SamplesToTime(numSideSample...
Definition: WaveClip.cpp:234
float GetRMS(size_t ii, double t0, double t1, bool mayThrow) const
Definition: WaveClip.cpp:423
double SnapToTrackSample(double time) const noexcept
Definition: WaveClip.cpp:1237
void StretchRightTo(double to)
Sets from the right to the absolute time (if in expected range)
Definition: WaveClip.cpp:311
bool EntirelyWithinPlayRegion(double t0, double t1) const
t0 and t1 both ∈ [...)
Definition: WaveClip.cpp:1389
void OffsetCutLines(double t0, double len)
Offset cutlines right to time 't0' by time amount 'len'.
Definition: WaveClip.cpp:1071
void SetTrimRight(double trim)
Sets the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:1315
std::pair< float, float > GetMinMax(size_t ii, double t0, double t1, bool mayThrow) const
Definition: WaveClip.cpp:399
void TrimRight(double deltaTime)
Moves play end position by deltaTime.
Definition: WaveClip.cpp:1330
bool AfterPlayRegion(double t) const
) <= t
Definition: WaveClip.cpp:1427
Envelope * GetEnvelope()
Definition: WaveClip.h:388
double SamplesToTime(sampleCount s) const noexcept
Definition: WaveClip.cpp:1232
const SampleBlockFactoryPtr & GetFactory()
Definition: WaveClip.cpp:372
void WriteXML(XMLWriter &xmlFile) const
Definition: WaveClip.cpp:642
sampleCount GetSequenceSamplesCount() const
Definition: WaveClip.cpp:1252
void ExpandCutLine(double cutLinePosition)
Definition: WaveClip.cpp:1020
void AppendSharedBlock(const std::shared_ptr< SampleBlock > &pBlock)
Definition: WaveClip.cpp:482
void ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
Definition: WaveClip.cpp:441
double GetPlayDuration() const
Definition: WaveClip.cpp:1279
void ClearSequence(double t0, double t1)
Definition: WaveClip.cpp:871
bool HasEqualStretchRatio(const WaveClip &other) const
Definition: WaveClip.cpp:349
WaveClip(const WaveClip &)=delete
void SetFloatAtTime(double t, size_t iChannel, float value, sampleFormat effectiveFormat)
Definition: WaveClip.cpp:243
bool IntersectsPlayRegion(double t0, double t1) const
[t0, t1) ∩ [...) != ∅
Definition: WaveClip.cpp:1403
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:145
void ClearAndAddCutLine(double t0, double t1)
Definition: WaveClip.cpp:934
bool IsEmpty() const
Definition: WaveClip.cpp:1284
sampleCount GetNumSamples() const
Definition: WaveClip.cpp:360
double mTrimLeft
Definition: WaveClip.h:606
WaveClipHolders mCutLines
Definition: WaveClip.h:636
void SetTrimLeft(double trim)
Sets the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:1305
double GetSequenceEndTime() const
Definition: WaveClip.cpp:1361
void Clear(double t0, double t1)
Definition: WaveClip.cpp:829
bool Paste(double t0, const WaveClip &other)
Definition: WaveClip.cpp:670
void UpdateEnvelopeTrackLen()
Definition: WaveClip.cpp:463
void StretchCutLines(double ratioChange)
Definition: WaveClip.cpp:328
size_t GetAppendBufferLen() const
Definition: WaveClip.cpp:266
void StretchLeftTo(double to)
Stretches from left to the absolute time (if in expected range)
Definition: WaveClip.cpp:294
bool PartlyWithinPlayRegion(double t0, double t1) const
t0 xor t1 ∈ [...)
Definition: WaveClip.cpp:1397
size_t GetWidth() const override
Definition: WaveClip.cpp:163
bool GetFloatAtTime(double t, size_t iChannel, float &value, bool mayThrow) const
Definition: WaveClip.cpp:200
void Flush()
Flush must be called after last Append.
Definition: WaveClip.cpp:514
std::vector< std::unique_ptr< Sequence > > mSequences
Definition: WaveClip.h:627
void TrimLeft(double deltaTime)
Moves play start position by deltaTime.
Definition: WaveClip.cpp:1325
double mClipStretchRatio
Definition: WaveClip.h:612
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
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
static RegisteredToolbarFactory factory
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
"finally" as in The C++ Programming Language, 4th ed., p. 358 Useful for defining ad-hoc RAII actions...
Definition: MemoryX.h:173
Restores state when an update loop over mSequences fails midway.
Definition: WaveClip.h:591
std::vector< std::unique_ptr< Sequence > > sequences
Definition: WaveClip.h:597
Transaction(WaveClip &clip)
Definition: WaveClip.cpp:1486
virtual void MarkChanged()=0
virtual ~WaveClipListener()=0
Definition: WaveClip.cpp:36
virtual void Invalidate()=0