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{
668 wxASSERT(nActions == WXSIZEOF(kActionStrings));
669
670 S.AddSpace(0, 5);
671
672 S.StartStatic(XO("Detect Silence"));
673 {
674 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
675 {
676 // Threshold
678 .Validator<FloatingPointValidator<double>>(
679 3, &mThresholdDB, NumValidatorStyle::NO_TRAILING_ZEROES,
681 .NameSuffix(XO("db"))
682 .AddTextBox(XXO("&Threshold:"), wxT(""), 0);
683 S.AddUnits(XO("dB"));
684
685 // Ignored silence
686 mInitialAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
688 NumValidatorStyle::NO_TRAILING_ZEROES,
690 .NameSuffix(XO("seconds"))
691 .AddTextBox(XXO("&Duration:"), wxT(""), 12);
692 S.AddUnits(XO("seconds"));
693 }
694 S.EndMultiColumn();
695 }
696 S.EndStatic();
697
698 S.StartStatic(XO("Action"));
699 {
700 S.StartHorizontalLay();
701 {
702 // Action choices
703 auto actionChoices = Msgids( kActionStrings, nActions );
705 .Validator<wxGenericValidator>(&mActionIndex)
706 .MinSize( { -1, -1 } )
707 .AddChoice( {}, actionChoices );
708 }
709 S.EndHorizontalLay();
710 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
711 {
712 // Truncation / Compression factor
713
714 mTruncLongestAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
716 NumValidatorStyle::NO_TRAILING_ZEROES,
718 .NameSuffix(XO("seconds"))
719 .AddTextBox(XXO("Tr&uncate to:"), wxT(""), 12);
720 S.AddUnits(XO("seconds"));
721
722 mSilenceCompressPercentT = S.Validator<FloatingPointValidator<double>>(
724 NumValidatorStyle::NO_TRAILING_ZEROES,
726 .NameSuffix(XO("%"))
727 .AddTextBox(XXO("C&ompress to:"), wxT(""), 12);
728 S.AddUnits(XO("%"));
729 }
730 S.EndMultiColumn();
731
732 S.StartMultiColumn(2, wxALIGN_CENTER_HORIZONTAL);
733 {
734 mIndependent = S.AddCheckBox(XXO("Trunc&ate tracks independently"),
736 }
737 S.EndMultiColumn();
738 }
739 S.EndStatic();
740
741 UpdateUI();
742 return nullptr;
743}
744
746{
747 mbIndependent = mIndependent->IsChecked();
748 return true;
749}
750
751// EffectTruncSilence implementation
752
753// Finds the intersection of the ordered region lists, stores in dest
755{
756 RegionList::iterator destIter;
757 destIter = dest.begin();
758 // Any time we reach the end of the dest list we're finished
759 if (destIter == dest.end())
760 return;
761 RegionList::iterator curDest = destIter;
762
763 // Operation: find non-silent regions in src, remove them from dest.
764 double nsStart = curDest->start;
765 double nsEnd;
766 bool lastRun = false; // must run the loop one extra time
767
768 RegionList::const_iterator srcIter = src.begin();
769
770 // This logic, causing the loop to run once after end of src, must occur
771 // each time srcIter is updated
772 if (srcIter == src.end())
773 {
774 lastRun = true;
775 }
776
777 while (srcIter != src.end() || lastRun)
778 {
779 // Don't use curSrc unless lastRun is false!
780 RegionList::const_iterator curSrc;
781
782 if (lastRun)
783 {
784 // The last non-silent region extends as far as possible
785 nsEnd = std::numeric_limits<double>::max();
786 }
787 else
788 {
789 curSrc = srcIter;
790 nsEnd = curSrc->start;
791 }
792
793 if (nsEnd > nsStart)
794 {
795 // Increment through dest until we have a region that could be affected
796 while (curDest->end <= nsStart)
797 {
798 ++destIter;
799 if (destIter == dest.end())
800 {
801 return;
802 }
803 curDest = destIter;
804 }
805
806 // Check for splitting dest region in two
807 if (nsStart > curDest->start && nsEnd < curDest->end)
808 {
809 // The second region
810 Region r(nsEnd, curDest->end);
811
812 // The first region
813 curDest->end = nsStart;
814
815 // Insert second region after first
816 RegionList::iterator nextIt(destIter);
817 ++nextIt;
818
819 // This should just read: destIter = dest.insert(nextIt, r); but we
820 // work around two two wxList::insert() bugs. First, in some
821 // versions it returns the wrong value. Second, in some versions,
822 // it crashes when you insert at list end.
823 if (nextIt == dest.end())
824 dest.push_back(r);
825 else
826 dest.insert(nextIt, r);
827 ++destIter; // (now points at the newly-inserted region)
828
829 curDest = destIter;
830 }
831
832 // Check for truncating the end of dest region
833 if (nsStart > curDest->start && nsStart < curDest->end &&
834 nsEnd >= curDest->end)
835 {
836 curDest->end = nsStart;
837
838 ++destIter;
839 if (destIter == dest.end())
840 {
841 return;
842 }
843 curDest = destIter;
844 }
845
846 // Check for all dest regions that need to be removed completely
847 while (nsStart <= curDest->start && nsEnd >= curDest->end)
848 {
849 destIter = dest.erase(destIter);
850 if (destIter == dest.end())
851 {
852 return;
853 }
854 curDest = destIter;
855 }
856
857 // Check for truncating the beginning of dest region
858 if (nsStart <= curDest->start &&
859 nsEnd > curDest->start && nsEnd < curDest->end)
860 {
861 curDest->start = nsEnd;
862 }
863 }
864
865 if (lastRun)
866 {
867 // done
868 lastRun = false;
869 }
870 else
871 {
872 // Next non-silent region starts at the end of this silent region
873 nsStart = curSrc->end;
874 ++srcIter;
875 if (srcIter == src.end())
876 {
877 lastRun = true;
878 }
879 }
880 }
881}
882
883/*
884void EffectTruncSilence::BlendFrames(float* buffer, int blendFrameCount, int leftIndex, int rightIndex)
885{
886 float* bufOutput = &buffer[leftIndex];
887 float* bufBefore = &buffer[leftIndex];
888 float* bufAfter = &buffer[rightIndex];
889 double beforeFactor = 1.0;
890 double afterFactor = 0.0;
891 double adjFactor = 1.0 / (double)blendFrameCount;
892 for (int j = 0; j < blendFrameCount; ++j)
893 {
894 bufOutput[j] = (float)((bufBefore[j] * beforeFactor) + (bufAfter[j] * afterFactor));
895 beforeFactor -= adjFactor;
896 afterFactor += adjFactor;
897 }
898}
899*/
900
902{
903 switch (mActionIndex)
904 {
905 case kTruncate:
906 mTruncLongestAllowedSilenceT->Enable(true);
907 mSilenceCompressPercentT->Enable(false);
908 break;
909 case kCompress:
910 mTruncLongestAllowedSilenceT->Enable(false);
911 mSilenceCompressPercentT->Enable(true);
912 }
913}
914
915void EffectTruncSilence::OnControlChange(wxCommandEvent & WXUNUSED(evt))
916{
917 mActionChoice->GetValidator()->TransferFromWindow();
918
919 UpdateUI();
920
921 if (!EnableApply(mUIParent->TransferDataFromWindow()))
922 {
923 return;
924 }
925}
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:570
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:502
FileConfig * gPrefs
Definition: Prefs.cpp:71
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:23
@ 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:107
std::shared_ptr< TrackList > mOutputTracks
Definition: EffectBase.h:105
const TrackList * inputTracks() const
Definition: EffectBase.h:102
double mT0
Definition: EffectBase.h:106
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: EffectBase.cpp:224
const AudacityProject * FindProject() const
Definition: EffectBase.cpp:303
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:739
int MessageBox(const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={}) const
Definition: Effect.cpp:869
bool EnableApply(bool enable=true)
Definition: Effect.cpp:613
bool LoadSettings(const CommandParameters &parms, EffectSettings &settings) const override
Restore settings from keys and values.
Definition: Effect.cpp:237
wxWindow * mUIParent
Definition: Effect.h:389
bool TotalProgress(double frac, const TranslatableString &={}) const
Definition: Effect.cpp:683
int GetNumWaveTracks() const
Definition: Effect.h:306
Performs effect computation.
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:104
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:124
static constexpr EffectParameter Independent
Definition: TruncSilence.h:132
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
wxTextCtrl * mThresholdText
Definition: TruncSilence.h:103
double mInitialAllowedSilence
Definition: TruncSilence.h:96
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:126
virtual ~EffectTruncSilence()
wxCheckBox * mIndependent
Definition: TruncSilence.h:108
bool TransferDataFromWindow(EffectSettings &settings) override
Update the given settings from controls.
double mSilenceCompressPercent
Definition: TruncSilence.h:98
static constexpr EffectParameter Truncate
Definition: TruncSilence.h:128
std::unique_ptr< EffectUIValidator > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access) override
Add controls to effect panel; always succeeds.
EffectType GetType() const override
Type determines how it behaves.
static const EnumValueSymbol kActionStrings[nActions]
Definition: TruncSilence.h:120
double mTruncLongestAllowedSilence
Definition: TruncSilence.h:97
static constexpr EffectParameter Threshold
Definition: TruncSilence.h:122
wxTextCtrl * mTruncLongestAllowedSilenceT
Definition: TruncSilence.h:106
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:105
wxTextCtrl * mSilenceCompressPercentT
Definition: TruncSilence.h:107
static constexpr EffectParameter Compress
Definition: TruncSilence.h:130
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:65
double LongSamplesToTime(sampleCount pos) const
Convert correctly between a number of samples and an (absolute) time in seconds.
Definition: SampleTrack.cpp:40
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: SampleTrack.cpp:35
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:631
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:383
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1330
iterator begin()
Definition: Track.h:1382
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1446
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1533
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:1806
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:2195
double GetEndTime() const override
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:2015
double GetRate() const override
Definition: WaveTrack.cpp:481
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
size_t as_size_t() const
Definition: SampleCount.cpp:17
double as_double() const
Definition: SampleCount.h:45
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for, if Traits<Type>::iterated_type is defined.
Definition: PackedArray.h:126
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