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