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