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