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
17#include "WaveClip.h"
18
19
20
21#include <math.h>
22#include <vector>
23#include <wx/log.h>
24
25#include "BasicUI.h"
26#include "Sequence.h"
27#include "Prefs.h"
28#include "Envelope.h"
29#include "Resample.h"
31#include "UserException.h"
32
33#ifdef _OPENMP
34#include <omp.h>
35#endif
36
38{
39}
40
42 sampleFormat format, int rate, int colourIndex)
43{
44 mRate = rate;
45 mColourIndex = colourIndex;
46 mSequence = std::make_unique<Sequence>( factory,
48
49 mEnvelope = std::make_unique<Envelope>(true, 1e-7, 2.0, 1.0);
50}
51
54 bool copyCutlines)
55{
56 // essentially a copy constructor - but you must pass in the
57 // current sample block factory, because we might be copying
58 // from one project to another
59
61 mTrimLeft = orig.mTrimLeft;
63 mRate = orig.mRate;
65 mSequence = std::make_unique<Sequence>(*orig.mSequence, factory);
66
67 mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
68
69 mName = orig.mName;
70
71 if ( copyCutlines )
72 for (const auto &clip: orig.mCutLines)
73 mCutLines.push_back
74 ( std::make_unique<WaveClip>( *clip, factory, true ) );
75
77}
78
81 bool copyCutlines,
82 double t0, double t1)
83{
84 assert(orig.CountSamples(t0, t1) > 0);
85
87
88 //Adjust trim values to sample-boundary
89 if(t0 > orig.GetPlayStartTime())
90 {
91 const auto s0 = orig.TimeToSamples(t0 - orig.GetSequenceStartTime());
92 mTrimLeft = orig.SamplesToTime(s0);
93
94 }else
95 mTrimLeft = orig.mTrimLeft;
96
97 if(t1 < orig.GetPlayEndTime())
98 {
99 const auto s1 = orig.TimeToSamples(orig.GetSequenceEndTime() - t1);
100 mTrimRight = orig.SamplesToTime(s1);
101 }
102 else
103 mTrimRight = orig.mTrimRight;
104
105 mRate = orig.mRate;
107
109
110 mSequence = std::make_unique<Sequence>(*orig.mSequence, factory);
111
112 mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
113
114 if ( copyCutlines )
115 for (const auto &cutline : orig.mCutLines)
116 mCutLines.push_back(std::make_unique<WaveClip>(*cutline, factory, true));
117}
118
119
121{
122}
123
125 sampleCount start, size_t len, bool mayThrow) const
126{
127 return mSequence->Get(buffer, format, start + TimeToSamples(mTrimLeft), len, mayThrow);
128}
129
132 sampleCount start, size_t len, sampleFormat effectiveFormat)
133{
134 // use Strong-guarantee
135 mSequence->SetSamples(buffer, format,
136 start + TimeToSamples(mTrimLeft), len, effectiveFormat);
137
138 // use No-fail-guarantee
139 MarkChanged();
140}
141
143{
144 return &mSequence->GetBlockArray();
145}
146
148{
149 return &mSequence->GetBlockArray();
150}
151
153{
155}
156
158{
159 return GetSequence()->GetAppendBuffer();
160}
161
162void WaveClip::MarkChanged() // NOFAIL-GUARANTEE
163{
164 Caches::ForEach( std::mem_fn( &WaveClipListener::MarkChanged ) );
165}
166
167std::pair<float, float> WaveClip::GetMinMax(
168 double t0, double t1, bool mayThrow) const
169{
170 t0 = std::max(t0, GetPlayStartTime());
171 t1 = std::min(t1, GetPlayEndTime());
172 if (t0 > t1) {
173 if (mayThrow)
175 return {
176 0.f, // harmless, but unused since Sequence::GetMinMax does not use these values
177 0.f // harmless, but unused since Sequence::GetMinMax does not use these values
178 };
179 }
180
181 if (t0 == t1)
182 return{ 0.f, 0.f };
183
184 auto s0 = TimeToSequenceSamples(t0);
185 auto s1 = TimeToSequenceSamples(t1);
186
187 return mSequence->GetMinMax(s0, s1-s0, mayThrow);
188}
189
190float WaveClip::GetRMS(double t0, double t1, bool mayThrow) const
191{
192 if (t0 > t1) {
193 if (mayThrow)
195 return 0.f;
196 }
197
198 if (t0 == t1)
199 return 0.f;
200
201 auto s0 = TimeToSequenceSamples(t0);
202 auto s1 = TimeToSequenceSamples(t1);
203
204 return mSequence->GetRMS(s0, s1-s0, mayThrow);
205}
206
208 const std::function<void(size_t)> & progressReport)
209{
210 // Note: it is not necessary to do this recursively to cutlines.
211 // They get converted as needed when they are expanded.
212
213 auto bChanged = mSequence->ConvertToSampleFormat(format, progressReport);
214 if (bChanged)
215 MarkChanged();
216}
217
220{
221 auto len = (mSequence->GetNumSamples().as_double()) / mRate;
222 if (len != mEnvelope->GetTrackLen())
223 mEnvelope->SetTrackLen(len, 1.0 / GetRate());
224}
225
227std::shared_ptr<SampleBlock> WaveClip::AppendNewBlock(
228 samplePtr buffer, sampleFormat format, size_t len)
229{
230 return mSequence->AppendNewBlock( buffer, format, len );
231}
232
234void WaveClip::AppendSharedBlock(const std::shared_ptr<SampleBlock> &pBlock)
235{
236 mSequence->AppendSharedBlock( pBlock );
237}
238
243 size_t len, unsigned int stride, sampleFormat effectiveFormat)
244{
245 //wxLogDebug(wxT("Append: len=%lli"), (long long) len);
246 auto cleanup = finally( [&] {
247 // use No-fail-guarantee
249 MarkChanged();
250 } );
251
252 return mSequence->Append(buffer, format, len, stride, effectiveFormat);
253}
254
261{
262 //wxLogDebug(wxT("WaveClip::Flush"));
263 //wxLogDebug(wxT(" mAppendBufferLen=%lli"), (long long) mAppendBufferLen);
264 //wxLogDebug(wxT(" previous sample count %lli"), (long long) mSequence->GetNumSamples());
265
266 if (GetAppendBufferLen() > 0) {
267
268 auto cleanup = finally( [&] {
270 MarkChanged();
271 } );
272
273 mSequence->Flush();
274 }
275
276 //wxLogDebug(wxT("now sample count %lli"), (long long) mSequence->GetNumSamples());
277}
278
279bool WaveClip::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
280{
281 if (tag == "waveclip")
282 {
283 double dblValue;
284 long longValue;
285 for (auto pair : attrs)
286 {
287 auto attr = pair.first;
288 auto value = pair.second;
289
290 if (attr == "offset")
291 {
292 if (!value.TryGet(dblValue))
293 return false;
294 SetSequenceStartTime(dblValue);
295 }
296 else if (attr == "trimLeft")
297 {
298 if (!value.TryGet(dblValue))
299 return false;
300 SetTrimLeft(dblValue);
301 }
302 else if (attr == "trimRight")
303 {
304 if (!value.TryGet(dblValue))
305 return false;
306 SetTrimRight(dblValue);
307 }
308 else if (attr == "name")
309 {
310 if(value.IsStringView())
311 SetName(value.ToWString());
312 }
313 else if (attr == "colorindex")
314 {
315 if (!value.TryGet(longValue))
316 return false;
317 SetColourIndex(longValue);
318 }
319 }
320 return true;
321 }
322
323 return false;
324}
325
326void WaveClip::HandleXMLEndTag(const std::string_view& tag)
327{
328 if (tag == "waveclip")
330}
331
332XMLTagHandler *WaveClip::HandleXMLChild(const std::string_view& tag)
333{
334 if (tag == "sequence")
335 return mSequence.get();
336 else if (tag == "envelope")
337 return mEnvelope.get();
338 else if (tag == "waveclip")
339 {
340 // Nested wave clips are cut lines
341 auto format = mSequence->GetSampleFormats().Stored();
342 // The format is not stored in WaveClip itself but passed to
343 // Sequence::Sequence; but then the Sequence will deserialize format
344 // again
345 mCutLines.push_back(
346 std::make_unique<WaveClip>(mSequence->GetFactory(),
347 format, mRate, 0 /*colourindex*/));
348 return mCutLines.back().get();
349 }
350 else
351 return NULL;
352}
353
354void WaveClip::WriteXML(XMLWriter &xmlFile) const
355// may throw
356{
357 xmlFile.StartTag(wxT("waveclip"));
358 xmlFile.WriteAttr(wxT("offset"), mSequenceOffset, 8);
359 xmlFile.WriteAttr(wxT("trimLeft"), mTrimLeft, 8);
360 xmlFile.WriteAttr(wxT("trimRight"), mTrimRight, 8);
361 xmlFile.WriteAttr(wxT("name"), mName);
362 xmlFile.WriteAttr(wxT("colorindex"), mColourIndex );
363
364 mSequence->WriteXML(xmlFile);
365 mEnvelope->WriteXML(xmlFile);
366
367 for (const auto &clip: mCutLines)
368 clip->WriteXML(xmlFile);
369
370 xmlFile.EndTag(wxT("waveclip"));
371}
372
374void WaveClip::Paste(double t0, const WaveClip* other)
375{
376 const bool clipNeedsResampling = other->mRate != mRate;
377 const bool clipNeedsNewFormat =
378 other->mSequence->GetSampleFormats().Stored()
379 != mSequence->GetSampleFormats().Stored();
380 std::unique_ptr<WaveClip> newClip;
381
382 t0 = std::clamp(t0, GetPlayStartTime(), GetPlayEndTime());
383
384 //seems like edge cases cannot happen, see WaveTrack::PasteWaveTrack
385 if (t0 == GetPlayStartTime())
386 {
388 SetTrimLeft(other->GetTrimLeft());
389
390 auto copy = std::make_unique<WaveClip>(*other, mSequence->GetFactory(), true);
391 copy->ClearSequence(copy->GetPlayEndTime(), copy->GetSequenceEndTime());
392 newClip = std::move(copy);
393 }
394 else if (t0 == GetPlayEndTime())
395 {
397 SetTrimRight(other->GetTrimRight());
398
399 auto copy = std::make_unique<WaveClip>(*other, mSequence->GetFactory(), true);
400 copy->ClearSequence(copy->GetSequenceStartTime(), copy->GetPlayStartTime());
401 newClip = std::move(copy);
402 }
403 else
404 {
405 newClip = std::make_unique<WaveClip>(*other, mSequence->GetFactory(), true);
406 newClip->ClearSequence(newClip->GetPlayEndTime(), newClip->GetSequenceEndTime());
407 newClip->ClearSequence(newClip->GetSequenceStartTime(), newClip->GetPlayStartTime());
408 newClip->SetTrimLeft(0);
409 newClip->SetTrimRight(0);
410 }
411
412 if (clipNeedsResampling || clipNeedsNewFormat)
413 {
414 auto copy = std::make_unique<WaveClip>(*newClip.get(), mSequence->GetFactory(), true);
415 if (clipNeedsResampling)
416 // The other clip's rate is different from ours, so resample
417 copy->Resample(mRate);
418 if (clipNeedsNewFormat)
419 // Force sample formats to match.
420 copy->ConvertToSampleFormat(mSequence->GetSampleFormats().Stored());
421 newClip = std::move(copy);
422 }
423
424 // Paste cut lines contained in pasted clip
425 WaveClipHolders newCutlines;
426 for (const auto &cutline: newClip->mCutLines)
427 {
428 auto cutlineCopy = std::make_unique<WaveClip>(*cutline, mSequence->GetFactory(),
429 // Recursively copy cutlines of cutlines. They don't need
430 // their offsets adjusted.
431 true);
432 cutlineCopy->Offset(t0 - GetSequenceStartTime());
433 newCutlines.push_back(std::move(cutlineCopy));
434 }
435
437
438 // Assume Strong-guarantee from Sequence::Paste
439 mSequence->Paste(s0, newClip->mSequence.get());
440
441 // Assume No-fail-guarantee in the remaining
442 MarkChanged();
443 auto sampleTime = 1.0 / GetRate();
444 mEnvelope->PasteEnvelope
445 (s0.as_double()/mRate + GetSequenceStartTime(), newClip->mEnvelope.get(), sampleTime);
446 OffsetCutLines(t0, newClip->GetPlayEndTime() - newClip->GetPlayStartTime());
447
448 for (auto &holder : newCutlines)
449 mCutLines.push_back(std::move(holder));
450}
451
453void WaveClip::InsertSilence( double t, double len, double *pEnvelopeValue )
454{
455 if (t == GetPlayStartTime() && t > GetSequenceStartTime())
457 else if (t == GetPlayEndTime() && t < GetSequenceEndTime()) {
459 SetTrimRight(.0);
460 }
461
462 auto s0 = TimeToSequenceSamples(t);
463 auto slen = (sampleCount)floor(len * mRate + 0.5);
464
465 // use Strong-guarantee
466 GetSequence()->InsertSilence(s0, slen);
467
468 // use No-fail-guarantee
469 OffsetCutLines(t, len);
470
471 const auto sampleTime = 1.0 / GetRate();
472 auto pEnvelope = GetEnvelope();
473 if ( pEnvelopeValue ) {
474
475 // Preserve limit value at the end
476 auto oldLen = pEnvelope->GetTrackLen();
477 auto newLen = oldLen + len;
478 pEnvelope->Cap( sampleTime );
479
480 // Ramp across the silence to the given value
481 pEnvelope->SetTrackLen( newLen, sampleTime );
482 pEnvelope->InsertOrReplace
483 ( pEnvelope->GetOffset() + newLen, *pEnvelopeValue );
484 }
485 else
486 pEnvelope->InsertSpace( t, len );
487
488 MarkChanged();
489}
490
492void WaveClip::AppendSilence( double len, double envelopeValue )
493{
494 auto t = GetPlayEndTime();
495 InsertSilence( t, len, &envelopeValue );
496}
497
499void WaveClip::Clear(double t0, double t1)
500{
501 auto st0 = t0;
502 auto st1 = t1;
503 auto offset = .0;
504 if (st0 <= GetPlayStartTime())
505 {
506 offset = (t0 - GetPlayStartTime()) + GetTrimLeft();
507 st0 = GetSequenceStartTime();
508
509 SetTrimLeft(.0);
510 }
511 if (st1 >= GetPlayEndTime())
512 {
513 st1 = GetSequenceEndTime();
514 SetTrimRight(.0);
515 }
516 ClearSequence(st0, st1);
517
518 if (offset != .0)
519 Offset(offset);
520}
521
523{
524 if (t > GetPlayStartTime() && t < GetPlayEndTime())
525 {
527 SetTrimLeft(.0);
529 }
530}
531
533{
534 if (t > GetPlayStartTime() && t < GetPlayEndTime())
535 {
537 SetTrimRight(.0);
538 }
539}
540
541void WaveClip::ClearSequence(double t0, double t1)
542{
543 auto clip_t0 = std::max(t0, GetSequenceStartTime());
544 auto clip_t1 = std::min(t1, GetSequenceEndTime());
545
546 auto s0 = TimeToSequenceSamples(clip_t0);
547 auto s1 = TimeToSequenceSamples(clip_t1);
548
549 if (s0 != s1)
550 {
551 // use Strong-guarantee
552 GetSequence()->Delete(s0, s1 - s0);
553
554 // use No-fail-guarantee in the remaining
555
556 // msmeyer
557 //
558 // Delete all cutlines that are within the given area, if any.
559 //
560 // Note that when cutlines are active, two functions are used:
561 // Clear() and ClearAndAddCutLine(). ClearAndAddCutLine() is called
562 // whenever the user directly calls a command that removes some audio, e.g.
563 // "Cut" or "Clear" from the menu. This command takes care about recursive
564 // preserving of cutlines within clips. Clear() is called when internal
565 // operations want to remove audio. In the latter case, it is the right
566 // thing to just remove all cutlines within the area.
567 //
568
569 // May DELETE as we iterate, so don't use range-for
570 for (auto it = mCutLines.begin(); it != mCutLines.end();)
571 {
572 WaveClip* clip = it->get();
573 double cutlinePosition = GetSequenceStartTime() + clip->GetSequenceStartTime();
574 if (cutlinePosition >= t0 && cutlinePosition <= t1)
575 {
576 // This cutline is within the area, DELETE it
577 it = mCutLines.erase(it);
578 }
579 else
580 {
581 if (cutlinePosition >= t1)
582 {
583 clip->Offset(clip_t0 - clip_t1);
584 }
585 ++it;
586 }
587 }
588
589 // Collapse envelope
590 auto sampleTime = 1.0 / GetRate();
591 GetEnvelope()->CollapseRegion(t0, t1, sampleTime);
592 }
593
594
595 MarkChanged();
596}
597
601void WaveClip::ClearAndAddCutLine(double t0, double t1)
602{
603 if (t0 > GetPlayEndTime() || t1 < GetPlayStartTime() || CountSamples(t0, t1) == 0)
604 return; // no samples to remove
605
606 const double clip_t0 = std::max( t0, GetPlayStartTime() );
607 const double clip_t1 = std::min( t1, GetPlayEndTime() );
608
609 auto newClip = std::make_unique< WaveClip >
610 (*this, mSequence->GetFactory(), true, clip_t0, clip_t1);
611
612 newClip->SetSequenceStartTime( clip_t0 - GetSequenceStartTime() );
613
614 // Remove cutlines from this clip that were in the selection, shift
615 // left those that were after the selection
616 // May DELETE as we iterate, so don't use range-for
617 for (auto it = mCutLines.begin(); it != mCutLines.end();)
618 {
619 WaveClip* clip = it->get();
620 double cutlinePosition = GetSequenceStartTime() + clip->GetSequenceStartTime();
621 if (cutlinePosition >= t0 && cutlinePosition <= t1)
622 it = mCutLines.erase(it);
623 else
624 {
625 if (cutlinePosition >= t1)
626 {
627 clip->Offset(clip_t0 - clip_t1);
628 }
629 ++it;
630 }
631 }
632
633 // Clear actual audio data
634 auto s0 = TimeToSequenceSamples(t0);
635 auto s1 = TimeToSequenceSamples(t1);
636
637 // use Weak-guarantee
638 GetSequence()->Delete(s0, s1-s0);
639
640 // Collapse envelope
641 auto sampleTime = 1.0 / GetRate();
642 GetEnvelope()->CollapseRegion( t0, t1, sampleTime );
643
644 MarkChanged();
645
646 mCutLines.push_back(std::move(newClip));
647}
648
649bool WaveClip::FindCutLine(double cutLinePosition,
650 double* cutlineStart /* = NULL */,
651 double* cutlineEnd /* = NULL */) const
652{
653 for (const auto &cutline: mCutLines)
654 {
655 if (fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001)
656 {
657 auto startTime = GetSequenceStartTime() + cutline->GetSequenceStartTime();
658 if (cutlineStart)
659 *cutlineStart = startTime;
660 if (cutlineEnd)
661 *cutlineEnd = startTime + cutline->SamplesToTime(cutline->GetPlaySamplesCount());
662 return true;
663 }
664 }
665
666 return false;
667}
668
670void WaveClip::ExpandCutLine(double cutLinePosition)
671{
672 auto end = mCutLines.end();
673 auto it = std::find_if( mCutLines.begin(), end,
674 [&](const WaveClipHolder &cutline) {
675 return fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001;
676 } );
677
678 if ( it != end ) {
679 auto cutline = it->get();
680 // assume Strong-guarantee from Paste
681
682 // Envelope::Paste takes offset into account, WaveClip::Paste doesn't!
683 // Do this to get the right result:
684 cutline->mEnvelope->SetOffset(0);
685
686 Paste(GetSequenceStartTime()+cutline->GetSequenceStartTime(), cutline);
687 // Now erase the cutline,
688 // but be careful to find it again, because Paste above may
689 // have modified the array of cutlines (if our cutline contained
690 // another cutline!), invalidating the iterator we had.
691 end = mCutLines.end();
692 it = std::find_if(mCutLines.begin(), end,
693 [=](const WaveClipHolder &p) { return p.get() == cutline; });
694 if (it != end)
695 mCutLines.erase(it); // deletes cutline!
696 else {
697 wxASSERT(false);
698 }
699 }
700}
701
702bool WaveClip::RemoveCutLine(double cutLinePosition)
703{
704 for (auto it = mCutLines.begin(); it != mCutLines.end(); ++it)
705 {
706 const auto &cutline = *it;
707 //std::numeric_limits<double>::epsilon() or (1.0 / static_cast<double>(mRate))?
708 if (fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001)
709 {
710 mCutLines.erase(it); // deletes cutline!
711 return true;
712 }
713 }
714
715 return false;
716}
717
719void WaveClip::OffsetCutLines(double t0, double len)
720{
721 for (const auto &cutLine : mCutLines)
722 {
723 if (GetSequenceStartTime() + cutLine->GetSequenceStartTime() >= t0)
724 cutLine->Offset(len);
725 }
726}
727
729{
731 for (const auto &cutline: mCutLines)
732 cutline->CloseLock();
733}
734
735void WaveClip::SetRate(int rate)
736{
737 const auto trimLeftSampleNum = TimeToSamples(mTrimLeft);
738 const auto trimRightSampleNum = TimeToSamples(mTrimRight);
739 mRate = rate;
740 mTrimLeft = SamplesToTime(trimLeftSampleNum);
741 mTrimRight = SamplesToTime(trimRightSampleNum);
742 auto newLength = mSequence->GetNumSamples().as_double() / mRate;
743 mEnvelope->RescaleTimes( newLength );
744 MarkChanged();
745}
746
749{
750 // Note: it is not necessary to do this recursively to cutlines.
751 // They get resampled as needed when they are expanded.
752
753 if (rate == mRate)
754 return; // Nothing to do
755
756 double factor = (double)rate / (double)mRate;
757 ::Resample resample(true, factor, factor); // constant rate resampling
758
759 const size_t bufsize = 65536;
760 Floats inBuffer{ bufsize };
761 Floats outBuffer{ bufsize };
762 sampleCount pos = 0;
763 bool error = false;
764 int outGenerated = 0;
765 auto numSamples = mSequence->GetNumSamples();
766
767 // This sequence is appended to below
768 auto newSequence = std::make_unique<Sequence>(
769 mSequence->GetFactory(), mSequence->GetSampleFormats());
770
776 while (pos < numSamples || outGenerated > 0)
777 {
778 const auto inLen = limitSampleBufferSize( bufsize, numSamples - pos );
779
780 bool isLast = ((pos + inLen) == numSamples);
781
782 if (!mSequence->Get((samplePtr)inBuffer.get(), floatSample, pos, inLen, true))
783 {
784 error = true;
785 break;
786 }
787
788 const auto results = resample.Process(factor, inBuffer.get(), inLen, isLast,
789 outBuffer.get(), bufsize);
790 outGenerated = results.second;
791
792 pos += results.first;
793
794 if (outGenerated < 0)
795 {
796 error = true;
797 break;
798 }
799
800 newSequence->Append((samplePtr)outBuffer.get(), floatSample,
801 outGenerated, 1,
802 widestSampleFormat /* computed samples need dither */
803 );
804
805 if (progress)
806 {
807 auto updateResult = progress->Poll(
808 pos.as_long_long(),
809 numSamples.as_long_long()
810 );
811 error = (updateResult != BasicUI::ProgressResult::Success);
812 if (error)
813 throw UserException{};
814 }
815 }
816
817 if (error)
820 XO("Resampling failed."),
821 XO("Warning"),
822 "Error:_Resampling"
823 };
824 else
825 {
826 // Use No-fail-guarantee in these steps
827 mSequence = std::move(newSequence);
828 mRate = rate;
829 Caches::ForEach( std::mem_fn( &WaveClipListener::Invalidate ) );
830 }
831}
832
833// Used by commands which interact with clips using the keyboard.
834// When two clips are immediately next to each other, the GetPlayEndTime()
835// of the first clip and the GetPlayStartTime() of the second clip may not
836// be exactly equal due to rounding errors.
838{
839 double endThis = GetRate() * GetPlayStartTime() + GetPlaySamplesCount().as_double();
840 double startNext = next->GetRate() * next->GetPlayStartTime();
841
842 // given that a double has about 15 significant digits, using a criterion
843 // of half a sample should be safe in all normal usage.
844 return fabs(startNext - endThis) < 0.5;
845}
846
847void WaveClip::SetName(const wxString& name)
848{
849 mName = name;
850}
851
852const wxString& WaveClip::GetName() const
853{
854 return mName;
855}
856
857sampleCount WaveClip::TimeToSamples(double time) const noexcept
858{
859 return sampleCount(floor(time * mRate + 0.5));
860}
861
862double WaveClip::SamplesToTime(sampleCount s) const noexcept
863{
864 return s.as_double() / mRate;
865}
866
868{
869 GetSequence()->SetSilence(TimeToSamples(GetTrimLeft()) + offset, length);
870 MarkChanged();
871}
872
874{
875 return mSequence->GetNumSamples();
876}
877
878double WaveClip::GetPlayStartTime() const noexcept
879{
881}
882
884{
886}
887
889{
890 auto numSamples = mSequence->GetNumSamples();
891
892 double maxLen = GetSequenceStartTime() + ((numSamples + GetAppendBufferLen()).as_double()) / mRate
894 // JS: calculated value is not the length;
895 // it is a maximum value and can be negative; no clipping to 0
896
897 return maxLen;
898}
899
901{
903}
904
906{
908}
909
911{
912 return mSequence->GetNumSamples()
914}
915
916void WaveClip::SetTrimLeft(double trim)
917{
918 mTrimLeft = std::max(.0, trim);
919}
920
921double WaveClip::GetTrimLeft() const noexcept
922{
923 return mTrimLeft;
924}
925
926void WaveClip::SetTrimRight(double trim)
927{
928 mTrimRight = std::max(.0, trim);
929}
930
931double WaveClip::GetTrimRight() const noexcept
932{
933 return mTrimRight;
934}
935
936void WaveClip::TrimLeft(double deltaTime)
937{
938 mTrimLeft += deltaTime;
939}
940
941void WaveClip::TrimRight(double deltaTime)
942{
943 mTrimRight += deltaTime;
944}
945
946void WaveClip::TrimLeftTo(double to)
947{
949}
950
952{
954}
955
956double WaveClip::GetSequenceStartTime() const noexcept
957{
958 // JS: mSequenceOffset is the minimum value and it is returned; no clipping to 0
959 return mSequenceOffset;
960}
961
962void WaveClip::SetSequenceStartTime(double startTime)
963{
964 mSequenceOffset = startTime;
965 mEnvelope->SetOffset(startTime);
966}
967
969{
970 auto numSamples = mSequence->GetNumSamples();
971
972 double maxLen = GetSequenceStartTime() + (numSamples + GetAppendBufferLen()).as_double() / mRate;
973 // JS: calculated value is not the length;
974 // it is a maximum value and can be negative; no clipping to 0
975
976 return maxLen;
977}
978
980{
982}
983
985{
986 return GetSequenceStartSample() + mSequence->GetNumSamples();
987}
988
989void WaveClip::Offset(double delta) noexcept
990{
991 SetSequenceStartTime(GetSequenceStartTime() + delta);
992}
993
994// Bug 2288 allowed overlapping clips.
995// This was a classic fencepost error.
996// We are within the clip if start < t <= end.
997// Note that BeforeClip and AfterClip must be consistent
998// with this definition.
999bool WaveClip::WithinPlayRegion(double t) const
1000{
1001 auto ts = TimeToSamples(t);
1002 return ts > GetPlayStartSample() && ts < GetPlayEndSample() + GetAppendBufferLen();
1003}
1004
1006{
1007 auto ts = TimeToSamples(t);
1008 return ts <= GetPlayStartSample();
1009}
1010
1011bool WaveClip::AfterPlayEndTime(double t) const
1012{
1013 auto ts = TimeToSamples(t);
1014 return ts >= GetPlayEndSample() + GetAppendBufferLen();
1015}
1016
1017sampleCount WaveClip::CountSamples(double t0, double t1) const
1018{
1019 if(t0 < t1)
1020 {
1021 t0 = std::max(t0, GetPlayStartTime());
1022 t1 = std::min(t1, GetPlayEndTime());
1023 const auto s0 = TimeToSamples(t0 - GetPlayStartTime());
1024 const auto s1 = TimeToSamples(t1 - GetPlayStartTime());
1025 return s1 - s0;
1026 }
1027 return { 0 };
1028}
1029
1031{
1032 if (t < GetSequenceStartTime())
1033 return 0;
1034 else if (t > GetSequenceEndTime())
1035 return mSequence->GetNumSamples();
1036 return TimeToSamples(t - GetSequenceStartTime());
1037}
1038
1040{
1041 return s - GetSequenceStartSample();
1042}
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
int format
Definition: ExportPCM.cpp:53
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:29
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:55
const char * constSamplePtr
Definition: SampleFormat.h:56
An AudacityException with no visible message.
std::shared_ptr< WaveClip > WaveClipHolder
Definition: WaveClip.h:41
std::vector< WaveClipHolder > WaveClipHolders
Definition: WaveClip.h:42
std::vector< Attribute > AttributesList
Definition: XMLTagHandler.h:40
Abstraction of a progress dialog with well defined time-to-completion estimate.
Definition: BasicUI.h:156
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:377
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:77
size_t GetAppendBufferLen() const
Definition: Sequence.h:220
void Delete(sampleCount start, sampleCount len)
Definition: Sequence.cpp:1524
void InsertSilence(sampleCount s0, sampleCount len)
Definition: Sequence.cpp:707
void SetSilence(sampleCount s0, sampleCount len)
Definition: Sequence.cpp:700
constSamplePtr GetAppendBuffer() const
Definition: Sequence.h:221
bool CloseLock()
Definition: Sequence.cpp:87
A MessageBoxException that shows a given, unvarying string.
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:101
bool WithinPlayRegion(double t) const
Definition: WaveClip.cpp:999
sampleCount GetSequenceEndSample() const
Returns the index of the sample next after the last sample of the underlying sequence.
Definition: WaveClip.cpp:984
constSamplePtr GetAppendBuffer() const
Definition: WaveClip.cpp:157
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
Definition: WaveClip.cpp:167
void ClearRight(double t)
Definition: WaveClip.cpp:532
bool BeforePlayStartTime(double t) const
Definition: WaveClip.cpp:1005
double GetSequenceStartTime() const noexcept
Definition: WaveClip.cpp:956
void SetPlayStartTime(double time)
Definition: WaveClip.cpp:883
void Resample(int rate, BasicUI::ProgressDialog *progress=NULL)
Definition: WaveClip.cpp:748
void SetSequenceStartTime(double startTime)
Definition: WaveClip.cpp:962
BlockArray * GetSequenceBlockArray()
Definition: WaveClip.cpp:142
bool mIsPlaceholder
Definition: WaveClip.h:374
void SetName(const wxString &name)
Definition: WaveClip.cpp:847
void MarkChanged()
Definition: WaveClip.cpp:162
bool RemoveCutLine(double cutLinePosition)
Remove cut line, without expanding the audio in it.
Definition: WaveClip.cpp:702
sampleCount TimeToSequenceSamples(double t) const
Definition: WaveClip.cpp:1030
std::unique_ptr< Sequence > mSequence
Definition: WaveClip.h:366
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride, sampleFormat effectiveFormat)
Definition: WaveClip.cpp:242
double GetPlayStartTime() const noexcept
Definition: WaveClip.cpp:878
std::unique_ptr< Envelope > mEnvelope
Definition: WaveClip.h:367
double GetTrimRight() const noexcept
Returns the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:931
double mTrimRight
Definition: WaveClip.h:361
wxString mName
Definition: WaveClip.h:377
std::shared_ptr< SampleBlock > AppendNewBlock(samplePtr buffer, sampleFormat format, size_t len)
For use in importing pre-version-3 projects to preserve sharing of blocks; no dithering applied.
Definition: WaveClip.cpp:227
int mRate
Definition: WaveClip.h:363
double GetTrimLeft() const noexcept
Returns the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:921
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
Definition: WaveClip.cpp:332
sampleCount CountSamples(double t0, double t1) const
Definition: WaveClip.cpp:1017
WaveClip(const WaveClip &) PROHIBITED
Sequence * GetSequence()
Definition: WaveClip.h:227
sampleCount GetPlayEndSample() const
Definition: WaveClip.cpp:905
const wxString & GetName() const
Definition: WaveClip.cpp:852
void ClearLeft(double t)
Definition: WaveClip.cpp:522
bool AfterPlayEndTime(double t) const
Definition: WaveClip.cpp:1011
void TrimLeftTo(double to)
Sets the the left trimming to the absolute time (if that is in bounds)
Definition: WaveClip.cpp:946
bool FindCutLine(double cutLinePosition, double *cutLineStart=NULL, double *cutLineEnd=NULL) const
Definition: WaveClip.cpp:649
sampleCount GetPlayStartSample() const
Definition: WaveClip.cpp:900
void AppendSilence(double len, double envelopeValue)
Definition: WaveClip.cpp:492
void SetRate(int rate)
Definition: WaveClip.cpp:735
void Paste(double t0, const WaveClip *other)
Paste data from other clip, resampling it if not equal rate.
Definition: WaveClip.cpp:374
sampleCount ToSequenceSamples(sampleCount s) const
Definition: WaveClip.cpp:1039
sampleCount GetSequenceStartSample() const
Returns the index of the first sample of the underlying sequence.
Definition: WaveClip.cpp:979
virtual ~WaveClip()
Definition: WaveClip.cpp:120
bool GetIsPlaceholder() const
Definition: WaveClip.h:334
int mColourIndex
Definition: WaveClip.h:364
void HandleXMLEndTag(const std::string_view &tag) override
Definition: WaveClip.cpp:326
double mSequenceOffset
Definition: WaveClip.h:359
void InsertSilence(double t, double len, double *pEnvelopeValue=nullptr)
Definition: WaveClip.cpp:453
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
Definition: WaveClip.cpp:279
float GetRMS(double t0, double t1, bool mayThrow=true) const
Definition: WaveClip.cpp:190
void SetColourIndex(int index)
Definition: WaveClip.h:149
void SetSilence(sampleCount offset, sampleCount length)
Silences the 'length' amount of samples starting from 'offset'(relative to the play start)
Definition: WaveClip.cpp:867
double GetPlayEndTime() const
Definition: WaveClip.cpp:888
bool GetSamples(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow=true) const
Definition: WaveClip.cpp:124
sampleCount TimeToSamples(double time) const noexcept
Definition: WaveClip.cpp:857
void TrimRightTo(double to)
Sets the the right trimming to the absolute time (if that is in bounds)
Definition: WaveClip.cpp:951
bool SharesBoundaryWithNextClip(const WaveClip *next) const
Definition: WaveClip.cpp:837
void OffsetCutLines(double t0, double len)
Offset cutlines right to time 't0' by time amount 'len'.
Definition: WaveClip.cpp:719
void SetTrimRight(double trim)
Sets the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:926
void TrimRight(double deltaTime)
Moves play end position by deltaTime.
Definition: WaveClip.cpp:941
Envelope * GetEnvelope()
Definition: WaveClip.h:219
void Offset(double delta) noexcept
Definition: WaveClip.cpp:989
double SamplesToTime(sampleCount s) const noexcept
Definition: WaveClip.cpp:862
sampleCount GetPlaySamplesCount() const
Definition: WaveClip.cpp:910
void WriteXML(XMLWriter &xmlFile) const
Definition: WaveClip.cpp:354
sampleCount GetSequenceSamplesCount() const
Returns the total number of samples in underlying sequence (not counting the cutlines)
Definition: WaveClip.cpp:873
void ExpandCutLine(double cutLinePosition)
Definition: WaveClip.cpp:670
void AppendSharedBlock(const std::shared_ptr< SampleBlock > &pBlock)
For use in importing pre-version-3 projects to preserve sharing of blocks.
Definition: WaveClip.cpp:234
int GetRate() const
Definition: WaveClip.h:140
void ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
Definition: WaveClip.cpp:207
void ClearSequence(double t0, double t1)
Definition: WaveClip.cpp:541
void SetSamples(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat)
Definition: WaveClip.cpp:131
void ClearAndAddCutLine(double t0, double t1)
Definition: WaveClip.cpp:601
double mTrimLeft
Definition: WaveClip.h:360
WaveClipHolders mCutLines
Definition: WaveClip.h:371
void SetTrimLeft(double trim)
Sets the play start offset in seconds from the beginning of the underlying sequence.
Definition: WaveClip.cpp:916
double GetSequenceEndTime() const
Definition: WaveClip.cpp:968
void Clear(double t0, double t1)
Definition: WaveClip.cpp:499
void UpdateEnvelopeTrackLen()
Definition: WaveClip.cpp:219
void CloseLock()
Definition: WaveClip.cpp:728
size_t GetAppendBufferLen() const
Definition: WaveClip.cpp:152
void Flush()
Flush must be called after last Append.
Definition: WaveClip.cpp:260
void TrimLeft(double deltaTime)
Moves play start position by deltaTime.
Definition: WaveClip.cpp:936
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
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
virtual void MarkChanged()=0
virtual ~WaveClipListener()=0
Definition: WaveClip.cpp:37
virtual void Invalidate()=0