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{
86 mTrimLeft = orig.mTrimLeft + (t0 > orig.GetPlayStartTime()? t0 - orig.GetPlayStartTime() : 0);
87 mTrimRight = orig.mTrimRight + (t1 < orig.GetPlayEndTime()? orig.GetPlayEndTime() - t1 : 0);
88
89 mRate = orig.mRate;
91
93
94 auto s0 = orig.TimeToSequenceSamples(t0);
95 auto s1 = orig.TimeToSequenceSamples(t1);
96
97 mSequence = std::make_unique<Sequence>(*orig.mSequence, factory);
98
99 mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
100
101 if ( copyCutlines )
102 // Copy cutline clips that fall in the range
103 for (const auto &ppClip : orig.mCutLines)
104 {
105 const WaveClip* clip = ppClip.get();
106 double cutlinePosition = orig.GetSequenceStartTime() + clip->GetSequenceStartTime();
107 if (cutlinePosition >= t0 && cutlinePosition <= t1)
108 {
109 auto newCutLine =
110 std::make_unique< WaveClip >( *clip, factory, true );
111 newCutLine->SetSequenceStartTime( cutlinePosition - t0 );
112 mCutLines.push_back(std::move(newCutLine));
113 }
114 }
115}
116
117
119{
120}
121
123 sampleCount start, size_t len, bool mayThrow) const
124{
125 return mSequence->Get(buffer, format, start + TimeToSamples(mTrimLeft), len, mayThrow);
126}
127
130 sampleCount start, size_t len)
131{
132 // use Strong-guarantee
133 mSequence->SetSamples(buffer, format, start + TimeToSamples(mTrimLeft), len);
134
135 // use No-fail-guarantee
136 MarkChanged();
137}
138
140{
141 return &mSequence->GetBlockArray();
142}
143
145{
146 return &mSequence->GetBlockArray();
147}
148
149void WaveClip::MarkChanged() // NOFAIL-GUARANTEE
150{
151 Caches::ForEach( std::mem_fn( &WaveClipListener::MarkChanged ) );
152}
153
154std::pair<float, float> WaveClip::GetMinMax(
155 double t0, double t1, bool mayThrow) const
156{
157 t0 = std::max(t0, GetPlayStartTime());
158 t1 = std::min(t1, GetPlayEndTime());
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 newClip->ClearSequence(newClip->GetPlayEndTime(), newClip->GetSequenceEndTime());
438 newClip->ClearSequence(newClip->GetSequenceStartTime(), newClip->GetPlayStartTime());
439 }
440
441 if (clipNeedsResampling || clipNeedsNewFormat)
442 {
443 auto copy = std::make_unique<WaveClip>(*newClip.get(), mSequence->GetFactory(), true);
444 if (clipNeedsResampling)
445 // The other clip's rate is different from ours, so resample
446 copy->Resample(mRate);
447 if (clipNeedsNewFormat)
448 // Force sample formats to match.
449 copy->ConvertToSampleFormat(mSequence->GetSampleFormat());
450 newClip = std::move(copy);
451 }
452
453 // Paste cut lines contained in pasted clip
454 WaveClipHolders newCutlines;
455 for (const auto &cutline: newClip->mCutLines)
456 {
457 auto cutlineCopy = std::make_unique<WaveClip>(*cutline, mSequence->GetFactory(),
458 // Recursively copy cutlines of cutlines. They don't need
459 // their offsets adjusted.
460 true);
461 cutlineCopy->Offset(t0 - GetSequenceStartTime());
462 newCutlines.push_back(std::move(cutlineCopy));
463 }
464
466
467 // Assume Strong-guarantee from Sequence::Paste
468 mSequence->Paste(s0, newClip->mSequence.get());
469
470 // Assume No-fail-guarantee in the remaining
471 MarkChanged();
472 auto sampleTime = 1.0 / GetRate();
473 mEnvelope->PasteEnvelope
474 (s0.as_double()/mRate + GetSequenceStartTime(), newClip->mEnvelope.get(), sampleTime);
475 OffsetCutLines(t0, newClip->GetPlayEndTime() - newClip->GetPlayStartTime());
476
477 for (auto &holder : newCutlines)
478 mCutLines.push_back(std::move(holder));
479}
480
482void WaveClip::InsertSilence( double t, double len, double *pEnvelopeValue )
483{
484 if (t == GetPlayStartTime() && t > GetSequenceStartTime())
486 else if (t == GetPlayEndTime() && t < GetSequenceEndTime()) {
488 SetTrimRight(.0);
489 }
490
491 auto s0 = TimeToSequenceSamples(t);
492 auto slen = (sampleCount)floor(len * mRate + 0.5);
493
494 // use Strong-guarantee
495 GetSequence()->InsertSilence(s0, slen);
496
497 // use No-fail-guarantee
498 OffsetCutLines(t, len);
499
500 const auto sampleTime = 1.0 / GetRate();
501 auto pEnvelope = GetEnvelope();
502 if ( pEnvelopeValue ) {
503
504 // Preserve limit value at the end
505 auto oldLen = pEnvelope->GetTrackLen();
506 auto newLen = oldLen + len;
507 pEnvelope->Cap( sampleTime );
508
509 // Ramp across the silence to the given value
510 pEnvelope->SetTrackLen( newLen, sampleTime );
511 pEnvelope->InsertOrReplace
512 ( pEnvelope->GetOffset() + newLen, *pEnvelopeValue );
513 }
514 else
515 pEnvelope->InsertSpace( t, len );
516
517 MarkChanged();
518}
519
521void WaveClip::AppendSilence( double len, double envelopeValue )
522{
523 auto t = GetPlayEndTime();
524 InsertSilence( t, len, &envelopeValue );
525}
526
528void WaveClip::Clear(double t0, double t1)
529{
530 auto st0 = t0;
531 auto st1 = t1;
532 auto offset = .0;
533 if (st0 <= GetPlayStartTime())
534 {
535 offset = (t0 - GetPlayStartTime()) + GetTrimLeft();
536 st0 = GetSequenceStartTime();
537
538 SetTrimLeft(.0);
539 }
540 if (st1 >= GetPlayEndTime())
541 {
542 st1 = GetSequenceEndTime();
543 SetTrimRight(.0);
544 }
545 ClearSequence(st0, st1);
546
547 if (offset != .0)
548 Offset(offset);
549}
550
552{
553 if (t > GetPlayStartTime() && t < GetPlayEndTime())
554 {
556 SetTrimLeft(.0);
558 }
559}
560
562{
563 if (t > GetPlayStartTime() && t < GetPlayEndTime())
564 {
566 SetTrimRight(.0);
567 }
568}
569
570void WaveClip::ClearSequence(double t0, double t1)
571{
572 auto clip_t0 = std::max(t0, GetSequenceStartTime());
573 auto clip_t1 = std::min(t1, GetSequenceEndTime());
574
575 auto s0 = TimeToSequenceSamples(clip_t0);
576 auto s1 = TimeToSequenceSamples(clip_t1);
577
578 if (s0 != s1)
579 {
580 // use Strong-guarantee
581 GetSequence()->Delete(s0, s1 - s0);
582
583 // use No-fail-guarantee in the remaining
584
585 // msmeyer
586 //
587 // Delete all cutlines that are within the given area, if any.
588 //
589 // Note that when cutlines are active, two functions are used:
590 // Clear() and ClearAndAddCutLine(). ClearAndAddCutLine() is called
591 // whenever the user directly calls a command that removes some audio, e.g.
592 // "Cut" or "Clear" from the menu. This command takes care about recursive
593 // preserving of cutlines within clips. Clear() is called when internal
594 // operations want to remove audio. In the latter case, it is the right
595 // thing to just remove all cutlines within the area.
596 //
597
598 // May DELETE as we iterate, so don't use range-for
599 for (auto it = mCutLines.begin(); it != mCutLines.end();)
600 {
601 WaveClip* clip = it->get();
602 double cutlinePosition = GetSequenceStartTime() + clip->GetSequenceStartTime();
603 if (cutlinePosition >= t0 && cutlinePosition <= t1)
604 {
605 // This cutline is within the area, DELETE it
606 it = mCutLines.erase(it);
607 }
608 else
609 {
610 if (cutlinePosition >= t1)
611 {
612 clip->Offset(clip_t0 - clip_t1);
613 }
614 ++it;
615 }
616 }
617
618 // Collapse envelope
619 auto sampleTime = 1.0 / GetRate();
620 GetEnvelope()->CollapseRegion(t0, t1, sampleTime);
621 }
622
623
624 MarkChanged();
625}
626
630void WaveClip::ClearAndAddCutLine(double t0, double t1)
631{
632 if (t0 > GetPlayEndTime() || t1 < GetPlayStartTime())
633 return; // time out of bounds
634
635 const double clip_t0 = std::max( t0, GetPlayStartTime() );
636 const double clip_t1 = std::min( t1, GetPlayEndTime() );
637
638 auto newClip = std::make_unique< WaveClip >
639 (*this, mSequence->GetFactory(), true, clip_t0, clip_t1);
640
641 newClip->SetSequenceStartTime( clip_t0 - GetSequenceStartTime() );
642
643 // Remove cutlines from this clip that were in the selection, shift
644 // left those that were after the selection
645 // May DELETE as we iterate, so don't use range-for
646 for (auto it = mCutLines.begin(); it != mCutLines.end();)
647 {
648 WaveClip* clip = it->get();
649 double cutlinePosition = GetSequenceStartTime() + clip->GetSequenceStartTime();
650 if (cutlinePosition >= t0 && cutlinePosition <= t1)
651 it = mCutLines.erase(it);
652 else
653 {
654 if (cutlinePosition >= t1)
655 {
656 clip->Offset(clip_t0 - clip_t1);
657 }
658 ++it;
659 }
660 }
661
662 // Clear actual audio data
663 auto s0 = TimeToSequenceSamples(t0);
664 auto s1 = TimeToSequenceSamples(t1);
665
666 // use Weak-guarantee
667 GetSequence()->Delete(s0, s1-s0);
668
669 // Collapse envelope
670 auto sampleTime = 1.0 / GetRate();
671 GetEnvelope()->CollapseRegion( t0, t1, sampleTime );
672
673 MarkChanged();
674
675 mCutLines.push_back(std::move(newClip));
676}
677
678bool WaveClip::FindCutLine(double cutLinePosition,
679 double* cutlineStart /* = NULL */,
680 double* cutlineEnd /* = NULL */) const
681{
682 for (const auto &cutline: mCutLines)
683 {
684 if (fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001)
685 {
686 auto startTime = GetSequenceStartTime() + cutline->GetSequenceStartTime();
687 if (cutlineStart)
688 *cutlineStart = startTime;
689 if (cutlineEnd)
690 *cutlineEnd = startTime + cutline->SamplesToTime(cutline->GetPlaySamplesCount());
691 return true;
692 }
693 }
694
695 return false;
696}
697
699void WaveClip::ExpandCutLine(double cutLinePosition)
700{
701 auto end = mCutLines.end();
702 auto it = std::find_if( mCutLines.begin(), end,
703 [&](const WaveClipHolder &cutline) {
704 return fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001;
705 } );
706
707 if ( it != end ) {
708 auto cutline = it->get();
709 // assume Strong-guarantee from Paste
710
711 // Envelope::Paste takes offset into account, WaveClip::Paste doesn't!
712 // Do this to get the right result:
713 cutline->mEnvelope->SetOffset(0);
714
715 Paste(GetSequenceStartTime()+cutline->GetSequenceStartTime(), cutline);
716 // Now erase the cutline,
717 // but be careful to find it again, because Paste above may
718 // have modified the array of cutlines (if our cutline contained
719 // another cutline!), invalidating the iterator we had.
720 end = mCutLines.end();
721 it = std::find_if(mCutLines.begin(), end,
722 [=](const WaveClipHolder &p) { return p.get() == cutline; });
723 if (it != end)
724 mCutLines.erase(it); // deletes cutline!
725 else {
726 wxASSERT(false);
727 }
728 }
729}
730
731bool WaveClip::RemoveCutLine(double cutLinePosition)
732{
733 for (auto it = mCutLines.begin(); it != mCutLines.end(); ++it)
734 {
735 const auto &cutline = *it;
736 //std::numeric_limits<double>::epsilon() or (1.0 / static_cast<double>(mRate))?
737 if (fabs(GetSequenceStartTime() + cutline->GetSequenceStartTime() - cutLinePosition) < 0.0001)
738 {
739 mCutLines.erase(it); // deletes cutline!
740 return true;
741 }
742 }
743
744 return false;
745}
746
748void WaveClip::OffsetCutLines(double t0, double len)
749{
750 for (const auto &cutLine : mCutLines)
751 {
752 if (GetSequenceStartTime() + cutLine->GetSequenceStartTime() >= t0)
753 cutLine->Offset(len);
754 }
755}
756
758{
760 for (const auto &cutline: mCutLines)
761 cutline->CloseLock();
762}
763
764void WaveClip::SetRate(int rate)
765{
766 mRate = rate;
767 auto newLength = mSequence->GetNumSamples().as_double() / mRate;
768 mEnvelope->RescaleTimes( newLength );
769 MarkChanged();
770}
771
774{
775 // Note: it is not necessary to do this recursively to cutlines.
776 // They get resampled as needed when they are expanded.
777
778 if (rate == mRate)
779 return; // Nothing to do
780
781 double factor = (double)rate / (double)mRate;
782 ::Resample resample(true, factor, factor); // constant rate resampling
783
784 const size_t bufsize = 65536;
785 Floats inBuffer{ bufsize };
786 Floats outBuffer{ bufsize };
787 sampleCount pos = 0;
788 bool error = false;
789 int outGenerated = 0;
790 auto numSamples = mSequence->GetNumSamples();
791
792 auto newSequence =
793 std::make_unique<Sequence>(mSequence->GetFactory(), mSequence->GetSampleFormat());
794
800 while (pos < numSamples || outGenerated > 0)
801 {
802 const auto inLen = limitSampleBufferSize( bufsize, numSamples - pos );
803
804 bool isLast = ((pos + inLen) == numSamples);
805
806 if (!mSequence->Get((samplePtr)inBuffer.get(), floatSample, pos, inLen, true))
807 {
808 error = true;
809 break;
810 }
811
812 const auto results = resample.Process(factor, inBuffer.get(), inLen, isLast,
813 outBuffer.get(), bufsize);
814 outGenerated = results.second;
815
816 pos += results.first;
817
818 if (outGenerated < 0)
819 {
820 error = true;
821 break;
822 }
823
824 newSequence->Append((samplePtr)outBuffer.get(), floatSample,
825 outGenerated);
826
827 if (progress)
828 {
829 auto updateResult = progress->Poll(
830 pos.as_long_long(),
831 numSamples.as_long_long()
832 );
833 error = (updateResult != BasicUI::ProgressResult::Success);
834 if (error)
835 throw UserException{};
836 }
837 }
838
839 if (error)
842 XO("Resampling failed."),
843 XO("Warning"),
844 "Error:_Resampling"
845 };
846 else
847 {
848 // Use No-fail-guarantee in these steps
849 mSequence = std::move(newSequence);
850 mRate = rate;
851 Caches::ForEach( std::mem_fn( &WaveClipListener::Invalidate ) );
852 }
853}
854
855// Used by commands which interact with clips using the keyboard.
856// When two clips are immediately next to each other, the GetPlayEndTime()
857// of the first clip and the GetPlayStartTime() of the second clip may not
858// be exactly equal due to rounding errors.
860{
861 double endThis = GetRate() * GetPlayStartTime() + GetPlaySamplesCount().as_double();
862 double startNext = next->GetRate() * next->GetPlayStartTime();
863
864 // given that a double has about 15 significant digits, using a criterion
865 // of half a sample should be safe in all normal usage.
866 return fabs(startNext - endThis) < 0.5;
867}
868
869void WaveClip::SetName(const wxString& name)
870{
871 mName = name;
872}
873
874const wxString& WaveClip::GetName() const
875{
876 return mName;
877}
878
879sampleCount WaveClip::TimeToSamples(double time) const noexcept
880{
881 return sampleCount(floor(time * mRate + 0.5));
882}
883
884double WaveClip::SamplesToTime(sampleCount s) const noexcept
885{
886 return s.as_double() / mRate;
887}
888
890{
891 GetSequence()->SetSilence(TimeToSamples(GetTrimLeft()) + offset, length);
892 MarkChanged();
893}
894
896{
897 return mSequence->GetNumSamples();
898}
899
900double WaveClip::GetPlayStartTime() const noexcept
901{
903}
904
906{
908}
909
911{
912 auto numSamples = mSequence->GetNumSamples();
913
914 double maxLen = GetSequenceStartTime() + ((numSamples + mAppendBufferLen).as_double()) / mRate
916 // JS: calculated value is not the length;
917 // it is a maximum value and can be negative; no clipping to 0
918
919 return maxLen;
920}
921
923{
925}
926
928{
930}
931
933{
934 return mSequence->GetNumSamples()
936}
937
938void WaveClip::SetTrimLeft(double trim)
939{
940 mTrimLeft = std::max(.0, trim);
941}
942
943double WaveClip::GetTrimLeft() const noexcept
944{
945 return mTrimLeft;
946}
947
948void WaveClip::SetTrimRight(double trim)
949{
950 mTrimRight = std::max(.0, trim);
951}
952
953double WaveClip::GetTrimRight() const noexcept
954{
955 return mTrimRight;
956}
957
958void WaveClip::TrimLeft(double deltaTime)
959{
960 mTrimLeft += deltaTime;
961}
962
963void WaveClip::TrimRight(double deltaTime)
964{
965 mTrimRight += deltaTime;
966}
967
968void WaveClip::TrimLeftTo(double to)
969{
971}
972
974{
976}
977
978double WaveClip::GetSequenceStartTime() const noexcept
979{
980 // JS: mSequenceOffset is the minimum value and it is returned; no clipping to 0
981 return mSequenceOffset;
982}
983
984void WaveClip::SetSequenceStartTime(double startTime)
985{
986 mSequenceOffset = startTime;
987 mEnvelope->SetOffset(startTime);
988}
989
991{
992 auto numSamples = mSequence->GetNumSamples();
993
994 double maxLen = GetSequenceStartTime() + (numSamples + mAppendBufferLen).as_double() / mRate;
995 // JS: calculated value is not the length;
996 // it is a maximum value and can be negative; no clipping to 0
997
998 return maxLen;
999}
1000
1002{
1004}
1005
1007{
1008 return GetSequenceStartSample() + mSequence->GetNumSamples();
1009}
1010
1011void WaveClip::Offset(double delta) noexcept
1012{
1013 SetSequenceStartTime(GetSequenceStartTime() + delta);
1014}
1015
1016// Bug 2288 allowed overlapping clips.
1017// This was a classic fencepost error.
1018// We are within the clip if start < t <= end.
1019// Note that BeforeClip and AfterClip must be consistent
1020// with this definition.
1021bool WaveClip::WithinPlayRegion(double t) const
1022{
1023 auto ts = TimeToSamples(t);
1024 return ts > GetPlayStartSample() && ts < GetPlayEndSample() + mAppendBufferLen;
1025}
1026
1028{
1029 auto ts = TimeToSamples(t);
1030 return ts <= GetPlayStartSample();
1031}
1032
1033bool WaveClip::AfterPlayEndTime(double t) const
1034{
1035 auto ts = TimeToSamples(t);
1036 return ts >= GetPlayEndSample() + mAppendBufferLen;
1037}
1038
1040{
1041 if (t < GetSequenceStartTime())
1042 return 0;
1043 else if (t > GetSequenceEndTime())
1044 return mSequence->GetNumSamples();
1045 return TimeToSamples(t - GetSequenceStartTime());
1046}
1047
1049{
1050 return s - GetSequenceStartSample();
1051}
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: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:22
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:1021
sampleCount GetSequenceEndSample() const
Returns the index of the sample next after the last sample of the underlying sequence.
Definition: WaveClip.cpp:1006
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
Definition: WaveClip.cpp:154
void ClearRight(double t)
Definition: WaveClip.cpp:561
bool BeforePlayStartTime(double t) const
Definition: WaveClip.cpp:1027
double GetSequenceStartTime() const noexcept
Definition: WaveClip.cpp:978
void SetPlayStartTime(double time)
Definition: WaveClip.cpp:905
void Resample(int rate, BasicUI::ProgressDialog *progress=NULL)
Definition: WaveClip.cpp:773
void SetSequenceStartTime(double startTime)
Definition: WaveClip.cpp:984
BlockArray * GetSequenceBlockArray()
Definition: WaveClip.cpp:139
bool mIsPlaceholder
Definition: WaveClip.h:355
void SetName(const wxString &name)
Definition: WaveClip.cpp:869
void MarkChanged()
Definition: WaveClip.cpp:149
bool RemoveCutLine(double cutLinePosition)
Remove cut line, without expanding the audio in it.
Definition: WaveClip.cpp:731
sampleCount TimeToSequenceSamples(double t) const
Definition: WaveClip.cpp:1039
std::unique_ptr< Sequence > mSequence
Definition: WaveClip.h:344
void SetSamples(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len)
Definition: WaveClip.cpp:129
double GetPlayStartTime() const noexcept
Definition: WaveClip.cpp:900
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:953
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:943
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:927
const wxString & GetName() const
Definition: WaveClip.cpp:874
void ClearLeft(double t)
Definition: WaveClip.cpp:551
bool AfterPlayEndTime(double t) const
Definition: WaveClip.cpp:1033
void TrimLeftTo(double to)
Sets the the left trimming to the absolute time (if that is in bounds)
Definition: WaveClip.cpp:968
bool FindCutLine(double cutLinePosition, double *cutLineStart=NULL, double *cutLineEnd=NULL) const
Definition: WaveClip.cpp:678
sampleCount GetPlayStartSample() const
Definition: WaveClip.cpp:922
void AppendSilence(double len, double envelopeValue)
Definition: WaveClip.cpp:521
void SetRate(int rate)
Definition: WaveClip.cpp:764
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:1048
sampleCount GetSequenceStartSample() const
Returns the index of the first sample of the underlying sequence.
Definition: WaveClip.cpp:1001
virtual ~WaveClip()
Definition: WaveClip.cpp:118
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:482
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:889
double GetPlayEndTime() const
Definition: WaveClip.cpp:910
bool GetSamples(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow=true) const
Definition: WaveClip.cpp:122
sampleCount TimeToSamples(double time) const noexcept
Definition: WaveClip.cpp:879
void TrimRightTo(double to)
Sets the the right trimming to the absolute time (if that is in bounds)
Definition: WaveClip.cpp:973
bool SharesBoundaryWithNextClip(const WaveClip *next) const
Definition: WaveClip.cpp:859
void OffsetCutLines(double t0, double len)
Offset cutlines right to time 't0' by time amount 'len'.
Definition: WaveClip.cpp:748
void SetTrimRight(double trim)
Sets the play end offset in seconds from the ending of the underlying sequence.
Definition: WaveClip.cpp:948
void TrimRight(double deltaTime)
Moves play end position by deltaTime.
Definition: WaveClip.cpp:963
Envelope * GetEnvelope()
Definition: WaveClip.h:205
void Offset(double delta) noexcept
Definition: WaveClip.cpp:1011
double SamplesToTime(sampleCount s) const noexcept
Definition: WaveClip.cpp:884
sampleCount GetPlaySamplesCount() const
Definition: WaveClip.cpp:932
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:895
void ExpandCutLine(double cutLinePosition)
Definition: WaveClip.cpp:699
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:570
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:630
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:938
double GetSequenceEndTime() const
Definition: WaveClip.cpp:990
void Clear(double t0, double t1)
Definition: WaveClip.cpp:528
void UpdateEnvelopeTrackLen()
Definition: WaveClip.cpp:206
void CloseLock()
Definition: WaveClip.cpp:757
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:958
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: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:39
virtual void Invalidate()=0