Audacity 3.2.0
TruncSilence.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 TruncSilence.cpp
6
7 Lynn Allan (from DM's Normalize)
8 Philip Van Baren (more options and boundary fixes)
9
10*******************************************************************//*******************************************************************/
17#include "TruncSilence.h"
18#include "EffectEditor.h"
19#include "LoadEffects.h"
20
21#include <algorithm>
22#include <list>
23#include <limits>
24#include <math.h>
25
26#include <wx/checkbox.h>
27#include <wx/choice.h>
28#include <wx/valgen.h>
29
30#include "Prefs.h"
31#include "Project.h"
32#include "ShuttleGui.h"
33#include "SyncLock.h"
34#include "WaveTrack.h"
35#include "../widgets/valnum.h"
36#include "AudacityMessageBox.h"
37
38class Enums {
39public:
40 static const size_t NumDbChoices;
41 static const EnumValueSymbol DbChoices[];
42};
43
45 // Table of text values, only for reading what was stored in legacy config
46 // files.
47 // It was inappropriate to make this a discrete choice control.
48 { wxT("-20 dB") },
49 { wxT("-25 dB") },
50 { wxT("-30 dB") },
51 { wxT("-35 dB") },
52 { wxT("-40 dB") },
53 { wxT("-45 dB") },
54 { wxT("-50 dB") },
55 { wxT("-55 dB") },
56 { wxT("-60 dB") },
57 { wxT("-65 dB") },
58 { wxT("-70 dB") },
59 { wxT("-75 dB") },
60 { wxT("-80 dB") }
61};
62
63// Map from position in table above to numerical value.
64static inline double enumToDB( int val ) { return -( 5.0 * val + 20.0 ); }
65
66const size_t Enums::NumDbChoices = WXSIZEOF(Enums::DbChoices);
67
69
70// Declaration of RegionList
71class RegionList : public std::list < Region > {};
72
74{
75 { XO("Truncate Detected Silence") },
76 { XO("Compress Excess Silence") }
77};
78
80 // Compatible with 2.1.0 and before
81 { wxT("0"), 0 }, // Remap to Truncate Detected Silence
82 { wxT("1"), 1 }, // Remap to Compress Excess Silence
83};
84
85static const size_t nObsoleteActions = WXSIZEOF( kObsoleteActions );
86
88{
91 > parameters;
92 return parameters;
93}
94
95static const size_t DEF_BlendFrameCount = 100;
96
97// Lower bound on the amount of silence to find at a time -- this avoids
98// detecting silence repeatedly in low-frequency sounds.
99static const double DEF_MinTruncMs = 0.001;
100
101// Typical fraction of total time taken by detection (better to guess low)
102const double detectFrac = 0.4;
103
105{ XO("Truncate Silence") };
106
108
109BEGIN_EVENT_TABLE(EffectTruncSilence, wxEvtHandler)
110 EVT_CHOICE(wxID_ANY, EffectTruncSilence::OnControlChange)
111 EVT_TEXT(wxID_ANY, EffectTruncSilence::OnControlChange)
113
115{
116 Parameters().Reset(*this);
117
118 SetLinearEffectFlag(false);
119
120 // This used to be changeable via the audacity.cfg/registry. Doubtful that was
121 // ever done.
122 //
123 // Original comment:
124 //
125 // mBlendFrameCount only retrieved from prefs ... not using dialog
126 // Only way to change (for windows) is thru registry
127 // The values should be figured dynamically ... too many frames could be invalid
128 mBlendFrameCount = DEF_BlendFrameCount;
129}
130
132{
133}
134
135// ComponentInterface implementation
136
138{
139 return Symbol;
140}
141
143{
144 return XO("Automatically reduces the length of passages where the volume is below a specified level");
145}
146
148{
149 return L"Truncate_Silence";
150}
151
152// EffectDefinitionInterface implementation
153
155{
156 return EffectTypeProcess;
157}
158
160 const CommandParameters & parms, EffectSettings &settings) const
161{
163
164 // A bit of special treatment for two parameters
165
166 // This control migrated from a choice to a text box in version 2.3.0
167 double myThreshold {};
168 bool newParams = [&] {
169 double temp;
170 if (!parms.ReadAndVerify(Threshold.key,
172 return false;
173 myThreshold = temp;
174 return true;
175 } ();
176
177 if ( !newParams ) {
178 int temp;
179 // Use legacy param:
180 if (!parms.ReadAndVerify(L"Db", &temp, 0,
182 return false;
183 myThreshold = enumToDB( temp );
184 }
185
186 {
187 int temp;
188 if (!parms.ReadAndVerify( ActIndex.key, &temp, ActIndex.def,
190 return false;
191
192 // TODO: fix this when settings are really externalized
193 const_cast<int&>(mActionIndex) = temp;
194 }
195 // TODO: fix this when settings are really externalized
196 const_cast<double&>(mThresholdDB) = myThreshold;
197 return true;
198}
199
200// Effect implementation
201
203 const EffectSettings &, double /* previewLength */) const
204{
205 double inputLength = mT1 - mT0;
206 double minInputLength = inputLength;
207
208 // Master list of silent regions
209 RegionList silences;
210
211 // Start with the whole selection silent
212 silences.push_back(Region(mT0, mT1));
213
214 int whichTrack = 0;
215
216 for (auto wt : inputTracks()->Selected< const WaveTrack >()) {
217 RegionList trackSilences;
218
219 auto index = wt->TimeToLongSamples(mT0);
220 sampleCount silentFrame = 0; // length of the current silence
221
222 Analyze(silences, trackSilences, wt, &silentFrame, &index, whichTrack, &inputLength, &minInputLength);
223
224 whichTrack++;
225 }
226 return inputLength;
227}
228
229
231{
232 const bool success =
235 : ProcessAll();
236
237 if (success)
239
240 return success;
241}
242
244{
245 unsigned nGroups = 0;
246
247 const bool syncLock = SyncLockState::Get(*FindProject()).IsSyncLocked();
248
249 // Check if it's permissible
250 {
251 for (auto track : inputTracks()->SelectedLeaders< const WaveTrack >() ) {
252 if (syncLock) {
253 auto channels = TrackList::Channels(track);
254 auto otherTracks =
255 SyncLock::Group(track).Filter<const WaveTrack>()
257 - [&](const Track *pTrack){
258 return channels.contains(pTrack); };
259 if (otherTracks) {
261 XO(
262"When truncating independently, there may only be one selected audio track in each Sync-Locked Track Group.") );
263 return false;
264 }
265 }
266
267 ++nGroups;
268 }
269 }
270
271 if (nGroups == 0)
272 // nothing to do
273 return true;
274
275 // Now do the work
276
277 // Copy tracks
278 CopyInputTracks(true);
279 double newT1 = 0.0;
280
281 {
282 unsigned iGroup = 0;
283 for (auto track : mOutputTracks->SelectedLeaders< WaveTrack >() ) {
284 Track *const last = *TrackList::Channels(track).rbegin();
285
286 RegionList silences;
287
288 if (!FindSilences(silences, mOutputTracks.get(), track, last))
289 return false;
290 // Treat tracks in the sync lock group only
291 Track *groupFirst, *groupLast;
292 if (syncLock) {
293 auto trackRange = SyncLock::Group(track);
294 groupFirst = *trackRange.begin();
295 groupLast = *trackRange.rbegin();
296 }
297 else {
298 groupFirst = track;
299 groupLast = last;
300 }
301 double totalCutLen = 0.0;
302 if (!DoRemoval(silences, iGroup, nGroups, groupFirst, groupLast, totalCutLen))
303 return false;
304 newT1 = std::max(newT1, mT1 - totalCutLen);
305
306 ++iGroup;
307 }
308 }
309
310 mT1 = newT1;
311
312 return true;
313}
314
316{
317 // Copy tracks
318 CopyInputTracks(true);
319
320 // Master list of silent regions.
321 // This list should always be kept in order.
322 RegionList silences;
323
324 auto trackRange0 = inputTracks()->Selected< const WaveTrack >();
325 if (FindSilences(
326 silences, inputTracks(), *trackRange0.begin(), *trackRange0.rbegin())) {
327 auto trackRange = mOutputTracks->Any();
328 double totalCutLen = 0.0;
329 if (DoRemoval(silences, 0, 1,
330 *trackRange.begin(), *trackRange.rbegin(), totalCutLen)) {
331 mT1 -= totalCutLen;
332 return true;
333 }
334 }
335
336 return false;
337}
338
340 (RegionList &silences, const TrackList *list,
341 const Track *firstTrack, const Track *lastTrack)
342{
343 // Start with the whole selection silent
344 silences.push_back(Region(mT0, mT1));
345
346 // Remove non-silent regions in each track
347 int whichTrack = 0;
348 for (auto wt :
349 list->Selected< const WaveTrack >()
350 .StartingWith( firstTrack ).EndingAfter( lastTrack ) )
351 {
352 // Smallest silent region to detect in frames
353 auto minSilenceFrames =
354 sampleCount(std::max(mInitialAllowedSilence, DEF_MinTruncMs) * wt->GetRate());
355
356 //
357 // Scan the track for silences
358 //
359 RegionList trackSilences;
360
361 auto index = wt->TimeToLongSamples(mT0);
362 sampleCount silentFrame = 0;
363
364 // Detect silences
365 bool cancelled = !(Analyze(silences, trackSilences, wt, &silentFrame, &index, whichTrack));
366
367 // Buffer has been freed, so we're OK to return if cancelled
368 if (cancelled)
369 {
371 return false;
372 }
373
374 if (silentFrame >= minSilenceFrames)
375 {
376 // Track ended in silence -- record region
377 trackSilences.push_back(Region(
378 wt->LongSamplesToTime(index - silentFrame),
379 wt->LongSamplesToTime(index)
380 ));
381 }
382
383 // Intersect with the overall silent region list
384 Intersect(silences, trackSilences);
385 whichTrack++;
386 }
387
388 return true;
389}
390
392(const RegionList &silences, unsigned iGroup, unsigned nGroups, Track *firstTrack, Track *lastTrack,
393 double &totalCutLen)
394{
395 //
396 // Now remove the silent regions from all selected / sync-lock selected tracks.
397 //
398
399 // Loop over detected regions in reverse (so cuts don't change time values
400 // down the line)
401 int whichReg = 0;
402 RegionList::const_reverse_iterator rit;
403 for (rit = silences.rbegin(); rit != silences.rend(); ++rit)
404 {
405 const Region &region = *rit;
406 const Region *const r = &region;
407
408 // Progress dialog and cancellation. Do additional cleanup before return.
409 const double frac = detectFrac +
410 (1 - detectFrac) * (iGroup + whichReg / double(silences.size())) / nGroups;
411 if (TotalProgress(frac))
412 {
414 return false;
415 }
416
417 // Intersection may create regions smaller than allowed; ignore them.
418 // Allow one nanosecond extra for consistent results with exact milliseconds of allowed silence.
419 if ((r->end - r->start) < (mInitialAllowedSilence - 0.000000001))
420 continue;
421
422 // Find NEW silence length as requested
423 double inLength = r->end - r->start;
424 double outLength;
425
426 switch (mActionIndex)
427 {
428 case kTruncate:
429 outLength = std::min(mTruncLongestAllowedSilence, inLength);
430 break;
431 case kCompress:
432 outLength = mInitialAllowedSilence +
433 (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0;
434 break;
435 default: // Not currently used.
436 outLength = std::min(mInitialAllowedSilence +
437 (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0,
439 }
440
441 const double cutLen = std::max(0.0, inLength - outLength);
442 // Don't waste time cutting nothing.
443 if( cutLen == 0.0 )
444 continue;
445
446 totalCutLen += cutLen;
447
448 double cutStart = (r->start + r->end - cutLen) / 2;
449 double cutEnd = cutStart + cutLen;
450 (mOutputTracks->Any()
451 .StartingWith(firstTrack).EndingAfter(lastTrack)
453 - [&](const Track *pTrack) { return
454 // Don't waste time past the end of a track
455 pTrack->GetEndTime() < r->start;
456 }
457 ).Visit(
458 [&](WaveTrack *wt) {
459
460 // In WaveTracks, clear with a cross-fade
461 auto blendFrames = mBlendFrameCount;
462 // Round start/end times to frame boundaries
463 cutStart = wt->LongSamplesToTime(wt->TimeToLongSamples(cutStart));
464 cutEnd = wt->LongSamplesToTime(wt->TimeToLongSamples(cutEnd));
465
466 // Make sure the cross-fade does not affect non-silent frames
467 if (wt->LongSamplesToTime(blendFrames) > inLength)
468 {
469 // Result is not more than blendFrames:
470 blendFrames = wt->TimeToLongSamples(inLength).as_size_t();
471 }
472
473 // Perform cross-fade in memory
474 Floats buf1{ blendFrames };
475 Floats buf2{ blendFrames };
476 auto t1 = wt->TimeToLongSamples(cutStart) - blendFrames / 2;
477 auto t2 = wt->TimeToLongSamples(cutEnd) - blendFrames / 2;
478
479 wt->GetFloats(buf1.get(), t1, blendFrames);
480 wt->GetFloats(buf2.get(), t2, blendFrames);
481
482 for (decltype(blendFrames) i = 0; i < blendFrames; ++i)
483 {
484 buf1[i] = ((blendFrames-i) * buf1[i] + i * buf2[i]) /
485 (double)blendFrames;
486 }
487
488 // Perform the cut
489 wt->Clear(cutStart, cutEnd);
490
491 // Write cross-faded data
492 wt->Set((samplePtr)buf1.get(), floatSample, t1, blendFrames,
493 // This effect mostly shifts samples to remove silences, and
494 // does only a little bit of floating point calculations to
495 // cross-fade the splices, over a 100 sample interval by default.
496 // Don't dither.
498 },
499 [&](Track *t) {
500 // Non-wave tracks: just do a sync-lock adjust
501 t->SyncLockAdjust(cutEnd, cutStart);
502 }
503 );
504 ++whichReg;
505 }
506
507 return true;
508}
509
511 RegionList& trackSilences,
512 const WaveTrack *wt,
513 sampleCount* silentFrame,
514 sampleCount* index,
515 int whichTrack,
516 double* inputLength /*= NULL*/,
517 double* minInputLength /*= NULL*/) const
518{
519 // Smallest silent region to detect in frames
520 auto minSilenceFrames = sampleCount(std::max( mInitialAllowedSilence, DEF_MinTruncMs) * wt->GetRate());
521
522 double truncDbSilenceThreshold = DB_TO_LINEAR( mThresholdDB );
523 auto blockLen = wt->GetMaxBlockSize();
524 auto start = wt->TimeToLongSamples(mT0);
525 auto end = wt->TimeToLongSamples(mT1);
526 sampleCount outLength = 0;
527
528 double previewLength;
529 gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLength, 6.0);
530 // Minimum required length in samples.
531 const sampleCount previewLen( previewLength * wt->GetRate() );
532
533 // Keep position in overall silences list for optimization
534 RegionList::iterator rit(silenceList.begin());
535
536 // Allocate buffer
537 Floats buffer{ blockLen };
538
539 // Loop through current track
540 while (*index < end) {
541 if (inputLength && ((outLength >= previewLen) || (*index - start > wt->TimeToLongSamples(*minInputLength)))) {
542 *inputLength = std::min<double>(*inputLength, *minInputLength);
543 if (outLength >= previewLen) {
544 *minInputLength = *inputLength;
545 }
546 return true;
547 }
548
549 if (!inputLength) {
550 // Show progress dialog, test for cancellation
551 bool cancelled = TotalProgress(
552 detectFrac * (whichTrack +
553 (*index - start).as_double() /
554 (end - start).as_double()) /
555 (double)GetNumWaveTracks());
556 if (cancelled)
557 return false;
558 }
559
560 // Optimization: if not in a silent region skip ahead to the next one
561
562 double curTime = wt->LongSamplesToTime(*index);
563 for ( ; rit != silenceList.end(); ++rit) {
564 // Find the first silent region ending after current time
565 if (rit->end >= curTime) {
566 break;
567 }
568 }
569
570 if (rit == silenceList.end()) {
571 // No more regions -- no need to process the rest of the track
572 if (inputLength) {
573 // Add available samples up to previewLength.
574 auto remainingTrackSamples = wt->TimeToLongSamples(wt->GetEndTime()) - *index;
575 auto requiredTrackSamples = previewLen - outLength;
576 outLength += (remainingTrackSamples > requiredTrackSamples)? requiredTrackSamples : remainingTrackSamples;
577 }
578
579 break;
580 }
581 else if (rit->start > curTime) {
582 // End current silent region, skip ahead
583 if (*silentFrame >= minSilenceFrames) {
584 trackSilences.push_back(Region(
585 wt->LongSamplesToTime(*index - *silentFrame),
586 wt->LongSamplesToTime(*index)
587 ));
588 }
589 *silentFrame = 0;
590 auto newIndex = wt->TimeToLongSamples(rit->start);
591 if (inputLength) {
592 auto requiredTrackSamples = previewLen - outLength;
593 // Add non-silent sample to outLength
594 outLength += ((newIndex - *index) > requiredTrackSamples)? requiredTrackSamples : newIndex - *index;
595 }
596
597 *index = newIndex;
598 }
599 // End of optimization
600
601 // Limit size of current block if we've reached the end
602 auto count = limitSampleBufferSize( blockLen, end - *index );
603
604 // Fill buffer
605 wt->GetFloats((buffer.get()), *index, count);
606
607 // Look for silenceList in current block
608 for (decltype(count) i = 0; i < count; ++i) {
609 if (inputLength && ((outLength >= previewLen) || (outLength > wt->TimeToLongSamples(*minInputLength)))) {
610 *inputLength = wt->LongSamplesToTime(*index + i) - wt->LongSamplesToTime(start);
611 break;
612 }
613
614 if (fabs(buffer[i]) < truncDbSilenceThreshold) {
615 (*silentFrame)++;
616 }
617 else {
618 sampleCount allowed = 0;
619 if (*silentFrame >= minSilenceFrames) {
620 if (inputLength) {
621 switch (mActionIndex) {
622 case kTruncate:
624 break;
625 case kCompress:
627 outLength += sampleCount(
628 allowed.as_double() +
629 (*silentFrame - allowed).as_double()
631 );
632 break;
633 // default: // Not currently used.
634 }
635 }
636
637 // Record the silent region
638 trackSilences.push_back(Region(
639 wt->LongSamplesToTime(*index + i - *silentFrame),
640 wt->LongSamplesToTime(*index + i)
641 ));
642 }
643 else if (inputLength) { // included as part of non-silence
644 outLength += *silentFrame;
645 }
646 *silentFrame = 0;
647 if (inputLength) {
648 ++outLength; // Add non-silent sample to outLength
649 }
650 }
651 }
652 // Next block
653 *index += count;
654 }
655
656 if (inputLength) {
657 *inputLength = std::min<double>(*inputLength, *minInputLength);
658 if (outLength >= previewLen) {
659 *minInputLength = *inputLength;
660 }
661 }
662
663 return true;
664}
665
666
667std::unique_ptr<EffectEditor> EffectTruncSilence::PopulateOrExchange(
669 const EffectOutputs *)
670{
671 mUIParent = S.GetParent();
672 wxASSERT(nActions == WXSIZEOF(kActionStrings));
673
674 S.AddSpace(0, 5);
675
676 S.StartStatic(XO("Detect Silence"));
677 {
678 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
679 {
680 // Threshold
682 .Validator<FloatingPointValidator<double>>(
683 3, &mThresholdDB, NumValidatorStyle::NO_TRAILING_ZEROES,
685 .NameSuffix(XO("db"))
686 .AddTextBox(XXO("&Threshold:"), wxT(""), 0);
687 S.AddUnits(XO("dB"));
688
689 // Ignored silence
690 mInitialAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
692 NumValidatorStyle::NO_TRAILING_ZEROES,
694 .NameSuffix(XO("seconds"))
695 .AddTextBox(XXO("&Duration:"), wxT(""), 12);
696 S.AddUnits(XO("seconds"));
697 }
698 S.EndMultiColumn();
699 }
700 S.EndStatic();
701
702 S.StartStatic(XO("Action"));
703 {
704 S.StartHorizontalLay();
705 {
706 // Action choices
707 auto actionChoices = Msgids( kActionStrings, nActions );
709 .Validator<wxGenericValidator>(&mActionIndex)
710 .MinSize( { -1, -1 } )
711 .AddChoice( {}, actionChoices );
712 }
713 S.EndHorizontalLay();
714 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
715 {
716 // Truncation / Compression factor
717
718 mTruncLongestAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
720 NumValidatorStyle::NO_TRAILING_ZEROES,
722 .NameSuffix(XO("seconds"))
723 .AddTextBox(XXO("Tr&uncate to:"), wxT(""), 12);
724 S.AddUnits(XO("seconds"));
725
726 mSilenceCompressPercentT = S.Validator<FloatingPointValidator<double>>(
728 NumValidatorStyle::NO_TRAILING_ZEROES,
730 .NameSuffix(XO("%"))
731 .AddTextBox(XXO("C&ompress to:"), wxT(""), 12);
732 S.AddUnits(XO("%"));
733 }
734 S.EndMultiColumn();
735
736 S.StartMultiColumn(2, wxALIGN_CENTER_HORIZONTAL);
737 {
738 mIndependent = S.AddCheckBox(XXO("Trunc&ate tracks independently"),
740 }
741 S.EndMultiColumn();
742 }
743 S.EndStatic();
744
745 UpdateUI();
746 return nullptr;
747}
748
750{
751 if (!mUIParent->TransferDataToWindow())
752 {
753 return false;
754 }
755
756 return true;
757}
758
760{
761 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
762 {
763 return false;
764 }
765
766 mbIndependent = mIndependent->IsChecked();
767
768 return true;
769}
770
771// EffectTruncSilence implementation
772
773// Finds the intersection of the ordered region lists, stores in dest
775{
776 RegionList::iterator destIter;
777 destIter = dest.begin();
778 // Any time we reach the end of the dest list we're finished
779 if (destIter == dest.end())
780 return;
781 RegionList::iterator curDest = destIter;
782
783 // Operation: find non-silent regions in src, remove them from dest.
784 double nsStart = curDest->start;
785 double nsEnd;
786 bool lastRun = false; // must run the loop one extra time
787
788 RegionList::const_iterator srcIter = src.begin();
789
790 // This logic, causing the loop to run once after end of src, must occur
791 // each time srcIter is updated
792 if (srcIter == src.end())
793 {
794 lastRun = true;
795 }
796
797 while (srcIter != src.end() || lastRun)
798 {
799 // Don't use curSrc unless lastRun is false!
800 RegionList::const_iterator curSrc;
801
802 if (lastRun)
803 {
804 // The last non-silent region extends as far as possible
805 nsEnd = std::numeric_limits<double>::max();
806 }
807 else
808 {
809 curSrc = srcIter;
810 nsEnd = curSrc->start;
811 }
812
813 if (nsEnd > nsStart)
814 {
815 // Increment through dest until we have a region that could be affected
816 while (curDest->end <= nsStart)
817 {
818 ++destIter;
819 if (destIter == dest.end())
820 {
821 return;
822 }
823 curDest = destIter;
824 }
825
826 // Check for splitting dest region in two
827 if (nsStart > curDest->start && nsEnd < curDest->end)
828 {
829 // The second region
830 Region r(nsEnd, curDest->end);
831
832 // The first region
833 curDest->end = nsStart;
834
835 // Insert second region after first
836 RegionList::iterator nextIt(destIter);
837 ++nextIt;
838
839 // This should just read: destIter = dest.insert(nextIt, r); but we
840 // work around two two wxList::insert() bugs. First, in some
841 // versions it returns the wrong value. Second, in some versions,
842 // it crashes when you insert at list end.
843 if (nextIt == dest.end())
844 dest.push_back(r);
845 else
846 dest.insert(nextIt, r);
847 ++destIter; // (now points at the newly-inserted region)
848
849 curDest = destIter;
850 }
851
852 // Check for truncating the end of dest region
853 if (nsStart > curDest->start && nsStart < curDest->end &&
854 nsEnd >= curDest->end)
855 {
856 curDest->end = nsStart;
857
858 ++destIter;
859 if (destIter == dest.end())
860 {
861 return;
862 }
863 curDest = destIter;
864 }
865
866 // Check for all dest regions that need to be removed completely
867 while (nsStart <= curDest->start && nsEnd >= curDest->end)
868 {
869 destIter = dest.erase(destIter);
870 if (destIter == dest.end())
871 {
872 return;
873 }
874 curDest = destIter;
875 }
876
877 // Check for truncating the beginning of dest region
878 if (nsStart <= curDest->start &&
879 nsEnd > curDest->start && nsEnd < curDest->end)
880 {
881 curDest->start = nsEnd;
882 }
883 }
884
885 if (lastRun)
886 {
887 // done
888 lastRun = false;
889 }
890 else
891 {
892 // Next non-silent region starts at the end of this silent region
893 nsStart = curSrc->end;
894 ++srcIter;
895 if (srcIter == src.end())
896 {
897 lastRun = true;
898 }
899 }
900 }
901}
902
903/*
904void EffectTruncSilence::BlendFrames(float* buffer, int blendFrameCount, int leftIndex, int rightIndex)
905{
906 float* bufOutput = &buffer[leftIndex];
907 float* bufBefore = &buffer[leftIndex];
908 float* bufAfter = &buffer[rightIndex];
909 double beforeFactor = 1.0;
910 double afterFactor = 0.0;
911 double adjFactor = 1.0 / (double)blendFrameCount;
912 for (int j = 0; j < blendFrameCount; ++j)
913 {
914 bufOutput[j] = (float)((bufBefore[j] * beforeFactor) + (bufAfter[j] * afterFactor));
915 beforeFactor -= adjFactor;
916 afterFactor += adjFactor;
917 }
918}
919*/
920
922{
923 switch (mActionIndex)
924 {
925 case kTruncate:
926 mTruncLongestAllowedSilenceT->Enable(true);
927 mSilenceCompressPercentT->Enable(false);
928 break;
929 case kCompress:
930 mTruncLongestAllowedSilenceT->Enable(false);
931 mSilenceCompressPercentT->Enable(true);
932 }
933}
934
935void EffectTruncSilence::OnControlChange(wxCommandEvent & WXUNUSED(evt))
936{
937 mActionChoice->GetValidator()->TransferFromWindow();
938
939 UpdateUI();
940
942 mUIParent, mUIParent->TransferDataFromWindow()))
943 {
944 return;
945 }
946}
947
949{
950 return false;
951}
wxT("CloseDown"))
END_EVENT_TABLE()
int min(int a, int b)
EffectType
@ EffectTypeProcess
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
auto Visit(Visitor &&vis, Variant &&var)
Mimic some of std::visit, for the case of one visitor only.
Definition: MemoryX.h:628
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:560
FileConfig * gPrefs
Definition: Prefs.cpp:70
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
char * samplePtr
Definition: SampleFormat.h:55
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Convenience function often useful when adding choice controls.
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:87
static CommandParameters::ObsoleteMap kObsoleteActions[]
static const size_t DEF_BlendFrameCount
WaveTrack::Region Region
static const size_t nObsoleteActions
const double detectFrac
static const double DEF_MinTruncMs
static double enumToDB(int val)
Generates EffectParameterMethods overrides from variadic template arguments.
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the SettingsVis...
bool ReadAndVerify(const wxString &key, float *val, float defVal, float min, float max) const
std::pair< wxString, size_t > ObsoleteMap
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
double mT1
Definition: EffectBase.h:113
std::shared_ptr< TrackList > mOutputTracks
Definition: EffectBase.h:111
const TrackList * inputTracks() const
Definition: EffectBase.h:108
double mT0
Definition: EffectBase.h:112
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: EffectBase.cpp:232
const AudacityProject * FindProject() const
Definition: EffectBase.cpp:311
static bool EnableApply(wxWindow *parent, bool enable=true)
Enable or disable the Apply button of the dialog that contains parent.
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:396
bool LoadSettings(const CommandParameters &parms, EffectSettings &settings) const override
Restore settings from keys and values.
Definition: Effect.cpp:130
bool TotalProgress(double frac, const TranslatableString &={}) const
Definition: Effect.cpp:338
int GetNumWaveTracks() const
Definition: Effect.h:140
Performs effect computation.
Hold values to send to effect output meters.
Interface for manipulations of an Effect's settings.
Truncate Silence automatically reduces the length of passages where the volume is below a set thresho...
Definition: TruncSilence.h:32
wxChoice * mActionChoice
Definition: TruncSilence.h:110
bool TransferDataToWindow(const EffectSettings &settings) override
std::unique_ptr< EffectEditor > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) override
Add controls to effect panel; always succeeds.
bool Process(EffectInstance &instance, EffectSettings &settings) override
bool FindSilences(RegionList &silences, const TrackList *list, const Track *firstTrack, const Track *lastTrack)
static constexpr EnumParameter ActIndex
Definition: TruncSilence.h:130
static constexpr EffectParameter Independent
Definition: TruncSilence.h:138
wxWeakRef< wxWindow > mUIParent
Definition: TruncSilence.h:98
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
wxTextCtrl * mThresholdText
Definition: TruncSilence.h:109
double mInitialAllowedSilence
Definition: TruncSilence.h:102
bool NeedsDither() const override
double CalcPreviewInputLength(const EffectSettings &settings, double previewLength) const override
bool Analyze(RegionList &silenceList, RegionList &trackSilences, const WaveTrack *wt, sampleCount *silentFrame, sampleCount *index, int whichTrack, double *inputLength=NULL, double *minInputLength=NULL) const
void OnControlChange(wxCommandEvent &evt)
static constexpr EffectParameter Minimum
Definition: TruncSilence.h:132
virtual ~EffectTruncSilence()
wxCheckBox * mIndependent
Definition: TruncSilence.h:114
bool TransferDataFromWindow(EffectSettings &settings) override
double mSilenceCompressPercent
Definition: TruncSilence.h:104
static constexpr EffectParameter Truncate
Definition: TruncSilence.h:134
EffectType GetType() const override
Type determines how it behaves.
static const EnumValueSymbol kActionStrings[nActions]
Definition: TruncSilence.h:126
double mTruncLongestAllowedSilence
Definition: TruncSilence.h:103
static constexpr EffectParameter Threshold
Definition: TruncSilence.h:128
wxTextCtrl * mTruncLongestAllowedSilenceT
Definition: TruncSilence.h:112
static const ComponentInterfaceSymbol Symbol
Definition: TruncSilence.h:36
const EffectParameterMethods & Parameters() const override
TranslatableString GetDescription() const override
ComponentInterfaceSymbol GetSymbol() const override
bool DoRemoval(const RegionList &silences, unsigned iGroup, unsigned nGroups, Track *firstTrack, Track *lastTrack, double &totalCutLen)
bool LoadSettings(const CommandParameters &parms, EffectSettings &settings) const override
Restore settings from keys and values.
wxTextCtrl * mInitialAllowedSilenceT
Definition: TruncSilence.h:111
wxTextCtrl * mSilenceCompressPercentT
Definition: TruncSilence.h:113
static constexpr EffectParameter Compress
Definition: TruncSilence.h:136
void Intersect(RegionList &dest, const RegionList &src)
static int DoMessageBox(const EffectPlugin &plugin, const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={})
static const size_t NumDbChoices
static const EnumValueSymbol DbChoices[]
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
Retrieve samples from a track in floating-point format, regardless of the storage format.
Definition: SampleTrack.h:82
double LongSamplesToTime(sampleCount pos) const
Convert correctly between a number of samples and an (absolute) time in seconds.
Definition: SampleTrack.cpp:48
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: SampleTrack.cpp:43
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
static bool IsSelectedOrSyncLockSelected(const Track *pTrack)
Definition: SyncLock.cpp:112
static TrackIterRange< Track > Group(Track *pTrack)
Definition: SyncLock.cpp:161
bool IsSyncLocked() const
Definition: SyncLock.cpp:43
static SyncLockState & Get(AudacityProject &project)
Definition: SyncLock.cpp:26
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:226
bool IsSelected() const
Definition: Track.cpp:403
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1339
iterator begin()
Definition: Track.h:1393
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1457
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1544
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
Definition: WaveTrack.h:51
size_t GetMaxBlockSize() const override
This returns a nonnegative number of samples meant to size a memory buffer.
Definition: WaveTrack.cpp:1611
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:652
double GetEndTime() const override
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1822
double GetRate() const override
Definition: WaveTrack.cpp:360
void Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Definition: WaveTrack.cpp:2002
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
size_t as_size_t() const
Definition: SampleCount.cpp:16
double as_double() const
Definition: SampleCount.h:46
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
BuiltinEffectsModule::Registration< EffectTruncSilence > reg
const wxChar *const key
Identifier in configuration file.
const Type def
Default value.
const Type min
Minimum value.
const Type max
Maximum value.
Externalized state of a plug-in.
Structure to hold region of a wavetrack and a comparison function for sortability.
Definition: WaveTrack.h:56