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 "BasicUI.h"
19#include "EffectEditor.h"
20#include "EffectOutputTracks.h"
21#include "LoadEffects.h"
22
23#include <algorithm>
24#include <list>
25#include <limits>
26#include <math.h>
27
28#include <wx/checkbox.h>
29#include <wx/choice.h>
30#include <wx/valgen.h>
31
32#include "Prefs.h"
33#include "Project.h"
34#include "ShuttleGui.h"
35#include "SyncLock.h"
36#include "WaveTrack.h"
37#include "../widgets/valnum.h"
38#include "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,
225 &inputLength, &minInputLength);
226
227 whichTrack += wt->NChannels();
228 }
229 return inputLength;
230}
231
232
234{
235 const bool success =
238 : ProcessAll();
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()->Selected<const WaveTrack>()) {
252 if (syncLock) {
253 auto otherTracks =
254 SyncLock::Group(*track).Filter<const WaveTrack>()
256 - [&](const Track *pTrack){ return pTrack == track; };
257 if (otherTracks) {
259 XO(
260"When truncating independently, there may only be one selected audio track in each Sync-Locked Track Group.") );
261 return false;
262 }
263 }
264
265 ++nGroups;
266 }
267 }
268
269 if (nGroups == 0)
270 // nothing to do
271 return true;
272
273 // Now do the work
274
275 // Copy tracks
276 EffectOutputTracks outputs {
277 *mTracks, GetType(), { { mT0, mT1 } }, true, true
278 };
279 double newT1 = 0.0;
280
281 {
282 unsigned iGroup = 0;
283 for (auto track : outputs.Get().Selected<WaveTrack>()) {
284 RegionList silences;
285 if (!FindSilences(silences,
286 TrackList::SingletonRange(&as_const(*track))))
287 return false;
288 // Treat tracks in the sync lock group only
289 Track *groupFirst, *groupLast;
290 auto range = syncLock
291 ? SyncLock::Group(*track)
292 : TrackList::SingletonRange<Track>(track);
293 double totalCutLen = 0.0;
294 if (!DoRemoval(silences, range, iGroup, nGroups, totalCutLen))
295 return false;
296 newT1 = std::max(newT1, mT1 - totalCutLen);
297
298 ++iGroup;
299 }
300 }
301
302 mT1 = newT1;
303
304 outputs.Commit();
305
306 return true;
307}
308
310{
311 // Copy tracks
312 EffectOutputTracks outputs {
313 *mTracks, GetType(), { { mT0, mT1 } }, true, true
314 };
315
316 // Master list of silent regions.
317 // This list should always be kept in order.
318 RegionList silences;
319
320 if (FindSilences(silences, outputs.Get().Selected<const WaveTrack>()))
321 {
322 auto trackRange = outputs.Get().Any();
323 double totalCutLen = 0.0;
324 if (DoRemoval(silences, trackRange, 0, 1, totalCutLen)) {
325 mT1 -= totalCutLen;
326 outputs.Commit();
327 return true;
328 }
329 }
330
331 return false;
332}
333
336{
337 // Start with the whole selection silent
338 silences.push_back(Region(mT0, mT1));
339
340 // Remove non-silent regions in each track
341 int whichTrack = 0;
342 for (auto wt : range) {
343 // Smallest silent region to detect in frames
344 auto minSilenceFrames =
346 * wt->GetRate());
347
348 //
349 // Scan the track for silences
350 //
351 RegionList trackSilences;
352
353 auto index = wt->TimeToLongSamples(mT0);
354 sampleCount silentFrame = 0;
355
356 // Detect silences
357 bool cancelled = !(Analyze(
358 silences, trackSilences, *wt, &silentFrame, &index, whichTrack));
359
360 // Buffer has been freed, so we're OK to return if cancelled
361 if (cancelled)
362 return false;
363
364 if (silentFrame >= minSilenceFrames)
365 {
366 // Track ended in silence -- record region
367 trackSilences.push_back(Region(
368 wt->LongSamplesToTime(index - silentFrame),
369 wt->LongSamplesToTime(index)
370 ));
371 }
372
373 // Intersect with the overall silent region list
374 Intersect(silences, trackSilences);
375 whichTrack++;
376 }
377
378 return true;
379}
380
382 const TrackIterRange<Track> &range,
383 unsigned iGroup, unsigned nGroups,
384 double &totalCutLen)
385{
386 //
387 // Now remove the silent regions from all selected / sync-lock selected tracks.
388 //
389
390 // Loop over detected regions in reverse (so cuts don't change time values
391 // down the line)
392 int whichReg = 0;
393 RegionList::const_reverse_iterator rit;
394 for (rit = silences.rbegin(); rit != silences.rend(); ++rit)
395 {
396 const Region &region = *rit;
397 const Region *const r = &region;
398
399 // Progress dialog and cancellation. Do additional cleanup before return.
400 const double frac = detectFrac +
401 (1 - detectFrac) * (iGroup + whichReg / double(silences.size())) / nGroups;
402 if (TotalProgress(frac))
403 return false;
404
405 // Intersection may create regions smaller than allowed; ignore them.
406 // Allow one nanosecond extra for consistent results with exact milliseconds of allowed silence.
407 if ((r->end - r->start) < (mInitialAllowedSilence - 0.000000001))
408 continue;
409
410 // Find NEW silence length as requested
411 double inLength = r->end - r->start;
412 double outLength;
413
414 switch (mActionIndex)
415 {
416 case kTruncate:
417 outLength = std::min(mTruncLongestAllowedSilence, inLength);
418 break;
419 case kCompress:
420 outLength = mInitialAllowedSilence +
421 (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0;
422 break;
423 default: // Not currently used.
424 outLength = std::min(mInitialAllowedSilence +
425 (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0,
427 }
428
429 const double cutLen = std::max(0.0, inLength - outLength);
430 // Don't waste time cutting nothing.
431 if( cutLen == 0.0 )
432 continue;
433
434 totalCutLen += cutLen;
435
436 bool success = true;
437 double cutStart = (r->start + r->end - cutLen) / 2;
438 double cutEnd = cutStart + cutLen;
439 (range
441 - [&](const Track *pTrack) { return
442 // Don't waste time past the end of a track
443 pTrack->GetEndTime() < r->start;
444 }
445 ).VisitWhile(success,
446 [&](WaveTrack &wt) {
447 // In WaveTracks, clear with a cross-fade
448 auto blendFrames = mBlendFrameCount;
449 // Round start/end times to frame boundaries
450 cutStart = wt.SnapToSample(cutStart);
451 cutEnd = wt.SnapToSample(cutEnd);
452
453 // Make sure the cross-fade does not affect non-silent frames
454 if (wt.LongSamplesToTime(blendFrames) > inLength) {
455 // Result is not more than blendFrames:
456 blendFrames = wt.TimeToLongSamples(inLength).as_size_t();
457 }
458
459 // Perform cross-fade in memory
460 struct Buffers {
461 Buffers(size_t size)
462 : buf1{ size }, buf2 { size }
463 {}
464 Floats buf1, buf2;
465 };
466 Buffers buffers[2]{ blendFrames, blendFrames };
467 auto t1 = wt.TimeToLongSamples(cutStart) - blendFrames / 2;
468 auto t2 = wt.TimeToLongSamples(cutEnd) - blendFrames / 2;
469
470 size_t iChannel = 0;
471 for (const auto pChannel : wt.Channels()) {
472 auto &buffer = buffers[iChannel];
473 pChannel->GetFloats(buffer.buf1.get(), t1, blendFrames);
474 pChannel->GetFloats(buffer.buf2.get(), t2, blendFrames);
475
476 for (decltype(blendFrames) i = 0; i < blendFrames; ++i) {
477 buffer.buf1[i] =
478 ((blendFrames - i) * buffer.buf1[i] + i * buffer.buf2[i]) /
479 (double)blendFrames;
480 }
481 ++iChannel;
482 }
483
484 wt.Clear(cutStart, cutEnd);
485
486 iChannel = 0;
487 for (const auto pChannel : wt.Channels()) {
488 // Write cross-faded data
489 auto &buffer = buffers[iChannel];
490 success = success &&
491 pChannel->SetFloats(buffer.buf1.get(), t1,
492 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 ++iChannel;
499 }
500 },
501 [&](Track &t) {
502 // Non-wave tracks: just do a sync-lock adjust
503 t.SyncLockAdjust(cutEnd, cutStart);
504 }
505 );
506 if (!success)
507 return false;
508 ++whichReg;
509 }
510
511 return true;
512}
513
515 RegionList& trackSilences, const WaveTrack &wt, sampleCount* silentFrame,
516 sampleCount* index, int whichTrack, double* inputLength,
517 double* minInputLength) const
518{
519 const auto rate = wt.GetRate();
520
521 // Smallest silent region to detect in frames
522 auto minSilenceFrames =
524
525 double truncDbSilenceThreshold = DB_TO_LINEAR(mThresholdDB);
526 auto blockLen = wt.GetMaxBlockSize();
527 auto start = wt.TimeToLongSamples(mT0);
528 auto end = wt.TimeToLongSamples(mT1);
529 sampleCount outLength = 0;
530
531 double previewLength;
532 gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLength, 6.0);
533 // Minimum required length in samples.
534 const sampleCount previewLen(previewLength * rate);
535
536 // Keep position in overall silences list for optimization
537 RegionList::iterator rit(silenceList.begin());
538
539 // Allocate buffers
540 Floats buffers[] {
541 Floats{ blockLen },
542 Floats{ blockLen }
543 };
544
545 // Loop through current track
546 while (*index < end) {
547 if (inputLength && ((outLength >= previewLen) ||
548 (*index - start > wt.TimeToLongSamples(*minInputLength)))
549 ) {
550 *inputLength = std::min<double>(*inputLength, *minInputLength);
551 if (outLength >= previewLen) {
552 *minInputLength = *inputLength;
553 }
554 return true;
555 }
556
557 if (!inputLength) {
558 // Show progress dialog, test for cancellation
559 bool cancelled = TotalProgress(
560 detectFrac * (whichTrack +
561 (*index - start).as_double() /
562 (end - start).as_double()) /
563 (double)GetNumWaveTracks());
564 if (cancelled)
565 return false;
566 }
567
568 // Optimization: if not in a silent region skip ahead to the next one
569
570 double curTime = wt.LongSamplesToTime(*index);
571 for ( ; rit != silenceList.end(); ++rit) {
572 // Find the first silent region ending after current time
573 if (rit->end >= curTime) {
574 break;
575 }
576 }
577
578 if (rit == silenceList.end()) {
579 // No more regions -- no need to process the rest of the track
580 if (inputLength) {
581 // Add available samples up to previewLength.
582 auto remainingTrackSamples =
583 wt.TimeToLongSamples(wt.GetEndTime()) - *index;
584 auto requiredTrackSamples = previewLen - outLength;
585 outLength += (remainingTrackSamples > requiredTrackSamples)
586 ? requiredTrackSamples
587 : remainingTrackSamples;
588 }
589
590 break;
591 }
592 else if (rit->start > curTime) {
593 // End current silent region, skip ahead
594 if (*silentFrame >= minSilenceFrames) {
595 trackSilences.push_back(Region(
596 wt.LongSamplesToTime(*index - *silentFrame),
597 wt.LongSamplesToTime(*index)
598 ));
599 }
600 *silentFrame = 0;
601 auto newIndex = wt.TimeToLongSamples(rit->start);
602 if (inputLength) {
603 auto requiredTrackSamples = previewLen - outLength;
604 // Add non-silent sample to outLength
605 outLength += ((newIndex - *index) > requiredTrackSamples)
606 ? requiredTrackSamples
607 : newIndex - *index;
608 }
609
610 *index = newIndex;
611 }
612 // End of optimization
613
614 // Limit size of current block if we've reached the end
615 auto count = limitSampleBufferSize( blockLen, end - *index );
616
617 // Fill buffers
618 size_t iChannel = 0;
619 for (const auto pChannel : wt.Channels())
620 pChannel->GetFloats(buffers[iChannel++].get(), *index, count);
621
622 // Look for silenceList in current block
623 for (decltype(count) i = 0; i < count; ++i) {
624 if (inputLength && ((outLength >= previewLen) ||
625 (outLength > wt.TimeToLongSamples(*minInputLength)))
626 ) {
627 *inputLength =
628 wt.LongSamplesToTime(*index + i) - wt.LongSamplesToTime(start);
629 break;
630 }
631
632 const bool silent = std::all_of(buffers, buffers + iChannel,
633 [&](const Floats &buffer){
634 return fabs(buffer[i]) < truncDbSilenceThreshold;
635 });
636 if (silent)
637 (*silentFrame)++;
638 else {
639 sampleCount allowed = 0;
640 if (*silentFrame >= minSilenceFrames) {
641 if (inputLength) {
642 switch (mActionIndex) {
643 case kTruncate:
644 outLength +=
646 break;
647 case kCompress:
648 allowed =
650 outLength += sampleCount(
651 allowed.as_double() +
652 (*silentFrame - allowed).as_double()
654 );
655 break;
656 // default: // Not currently used.
657 }
658 }
659
660 // Record the silent region
661 trackSilences.push_back(Region(
662 wt.LongSamplesToTime(*index + i - *silentFrame),
663 wt.LongSamplesToTime(*index + i)
664 ));
665 }
666 else if (inputLength) { // included as part of non-silence
667 outLength += *silentFrame;
668 }
669 *silentFrame = 0;
670 if (inputLength) {
671 ++outLength; // Add non-silent sample to outLength
672 }
673 }
674 }
675 // Next block
676 *index += count;
677 }
678
679 if (inputLength) {
680 *inputLength = std::min<double>(*inputLength, *minInputLength);
681 if (outLength >= previewLen) {
682 *minInputLength = *inputLength;
683 }
684 }
685
686 return true;
687}
688
689
690std::unique_ptr<EffectEditor> EffectTruncSilence::PopulateOrExchange(
692 const EffectOutputs *)
693{
694 mUIParent = S.GetParent();
695 wxASSERT(nActions == WXSIZEOF(kActionStrings));
696
697 S.AddSpace(0, 5);
698
699 S.StartStatic(XO("Detect Silence"));
700 {
701 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
702 {
703 // Threshold
705 .Validator<FloatingPointValidator<double>>(
706 3, &mThresholdDB, NumValidatorStyle::NO_TRAILING_ZEROES,
708 .NameSuffix(XO("db"))
709 .AddTextBox(XXO("&Threshold:"), wxT(""), 0);
710 S.AddUnits(XO("dB"));
711
712 // Ignored silence
713 mInitialAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
715 NumValidatorStyle::NO_TRAILING_ZEROES,
717 .NameSuffix(XO("seconds"))
718 .AddTextBox(XXO("&Duration:"), wxT(""), 12);
719 S.AddUnits(XO("seconds"));
720 }
721 S.EndMultiColumn();
722 }
723 S.EndStatic();
724
725 S.StartStatic(XO("Action"));
726 {
727 S.StartHorizontalLay();
728 {
729 // Action choices
730 auto actionChoices = Msgids( kActionStrings, nActions );
732 .Validator<wxGenericValidator>(&mActionIndex)
733 .MinSize( { -1, -1 } )
734 .AddChoice( {}, actionChoices );
735 }
736 S.EndHorizontalLay();
737 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
738 {
739 // Truncation / Compression factor
740
741 mTruncLongestAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
743 NumValidatorStyle::NO_TRAILING_ZEROES,
745 .NameSuffix(XO("seconds"))
746 .AddTextBox(XXO("Tr&uncate to:"), wxT(""), 12);
747 S.AddUnits(XO("seconds"));
748
749 mSilenceCompressPercentT = S.Validator<FloatingPointValidator<double>>(
751 NumValidatorStyle::NO_TRAILING_ZEROES,
753 .NameSuffix(XO("%"))
754 .AddTextBox(XXO("C&ompress to:"), wxT(""), 12);
755 S.AddUnits(XO("%"));
756 }
757 S.EndMultiColumn();
758
759 S.StartMultiColumn(2, wxALIGN_CENTER_HORIZONTAL);
760 {
761 mIndependent = S.AddCheckBox(XXO("Trunc&ate tracks independently"),
763 }
764 S.EndMultiColumn();
765 }
766 S.EndStatic();
767
768 UpdateUI();
769 return nullptr;
770}
771
773{
774 if (!mUIParent->TransferDataToWindow())
775 {
776 return false;
777 }
778
779 return true;
780}
781
783{
784 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
785 {
786 return false;
787 }
788
789 mbIndependent = mIndependent->IsChecked();
790
791 return true;
792}
793
794// EffectTruncSilence implementation
795
796// Finds the intersection of the ordered region lists, stores in dest
798{
799 RegionList::iterator destIter;
800 destIter = dest.begin();
801 // Any time we reach the end of the dest list we're finished
802 if (destIter == dest.end())
803 return;
804 RegionList::iterator curDest = destIter;
805
806 // Operation: find non-silent regions in src, remove them from dest.
807 double nsStart = curDest->start;
808 double nsEnd;
809 bool lastRun = false; // must run the loop one extra time
810
811 RegionList::const_iterator srcIter = src.begin();
812
813 // This logic, causing the loop to run once after end of src, must occur
814 // each time srcIter is updated
815 if (srcIter == src.end())
816 {
817 lastRun = true;
818 }
819
820 while (srcIter != src.end() || lastRun)
821 {
822 // Don't use curSrc unless lastRun is false!
823 RegionList::const_iterator curSrc;
824
825 if (lastRun)
826 {
827 // The last non-silent region extends as far as possible
828 nsEnd = std::numeric_limits<double>::max();
829 }
830 else
831 {
832 curSrc = srcIter;
833 nsEnd = curSrc->start;
834 }
835
836 if (nsEnd > nsStart)
837 {
838 // Increment through dest until we have a region that could be affected
839 while (curDest->end <= nsStart)
840 {
841 ++destIter;
842 if (destIter == dest.end())
843 {
844 return;
845 }
846 curDest = destIter;
847 }
848
849 // Check for splitting dest region in two
850 if (nsStart > curDest->start && nsEnd < curDest->end)
851 {
852 // The second region
853 Region r(nsEnd, curDest->end);
854
855 // The first region
856 curDest->end = nsStart;
857
858 // Insert second region after first
859 RegionList::iterator nextIt(destIter);
860 ++nextIt;
861
862 // This should just read: destIter = dest.insert(nextIt, r); but we
863 // work around two two wxList::insert() bugs. First, in some
864 // versions it returns the wrong value. Second, in some versions,
865 // it crashes when you insert at list end.
866 if (nextIt == dest.end())
867 dest.push_back(r);
868 else
869 dest.insert(nextIt, r);
870 ++destIter; // (now points at the newly-inserted region)
871
872 curDest = destIter;
873 }
874
875 // Check for truncating the end of dest region
876 if (nsStart > curDest->start && nsStart < curDest->end &&
877 nsEnd >= curDest->end)
878 {
879 curDest->end = nsStart;
880
881 ++destIter;
882 if (destIter == dest.end())
883 {
884 return;
885 }
886 curDest = destIter;
887 }
888
889 // Check for all dest regions that need to be removed completely
890 while (nsStart <= curDest->start && nsEnd >= curDest->end)
891 {
892 destIter = dest.erase(destIter);
893 if (destIter == dest.end())
894 {
895 return;
896 }
897 curDest = destIter;
898 }
899
900 // Check for truncating the beginning of dest region
901 if (nsStart <= curDest->start &&
902 nsEnd > curDest->start && nsEnd < curDest->end)
903 {
904 curDest->start = nsEnd;
905 }
906 }
907
908 if (lastRun)
909 {
910 // done
911 lastRun = false;
912 }
913 else
914 {
915 // Next non-silent region starts at the end of this silent region
916 nsStart = curSrc->end;
917 ++srcIter;
918 if (srcIter == src.end())
919 {
920 lastRun = true;
921 }
922 }
923 }
924}
925
926/*
927void EffectTruncSilence::BlendFrames(float* buffer, int blendFrameCount, int leftIndex, int rightIndex)
928{
929 float* bufOutput = &buffer[leftIndex];
930 float* bufBefore = &buffer[leftIndex];
931 float* bufAfter = &buffer[rightIndex];
932 double beforeFactor = 1.0;
933 double afterFactor = 0.0;
934 double adjFactor = 1.0 / (double)blendFrameCount;
935 for (int j = 0; j < blendFrameCount; ++j)
936 {
937 bufOutput[j] = (float)((bufBefore[j] * beforeFactor) + (bufAfter[j] * afterFactor));
938 beforeFactor -= adjFactor;
939 afterFactor += adjFactor;
940 }
941}
942*/
943
945{
946 switch (mActionIndex)
947 {
948 case kTruncate:
949 mTruncLongestAllowedSilenceT->Enable(true);
950 mSilenceCompressPercentT->Enable(false);
951 break;
952 case kCompress:
953 mTruncLongestAllowedSilenceT->Enable(false);
954 mSilenceCompressPercentT->Enable(true);
955 }
956}
957
958void EffectTruncSilence::OnControlChange(wxCommandEvent & WXUNUSED(evt))
959{
960 mActionChoice->GetValidator()->TransferFromWindow();
961
962 UpdateUI();
963
965 mUIParent, mUIParent->TransferDataFromWindow()))
966 {
967 return;
968 }
969}
970
972{
973 return false;
974}
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
END_EVENT_TABLE()
int min(int a, int b)
EffectType
@ EffectTypeProcess
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:338
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
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.
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:51
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:114
const TrackList * inputTracks() const
Definition: EffectBase.h:91
std::shared_ptr< TrackList > mTracks
Definition: EffectBase.h:107
double mT0
Definition: EffectBase.h:113
const AudacityProject * FindProject() const
Definition: EffectBase.cpp:220
static bool EnableApply(wxWindow *parent, bool enable=true)
Enable or disable the Apply button of the dialog that contains parent.
bool LoadSettings(const CommandParameters &parms, EffectSettings &settings) const override
Restore settings from keys and values.
Definition: Effect.cpp:122
bool TotalProgress(double frac, const TranslatableString &={}) const
Definition: Effect.cpp:335
int GetNumWaveTracks() const
Definition: Effect.h:139
Performs effect computation.
Use this object to copy the input tracks to tentative outputTracks.
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:33
wxChoice * mActionChoice
Definition: TruncSilence.h:107
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
static constexpr EnumParameter ActIndex
Definition: TruncSilence.h:127
static constexpr EffectParameter Independent
Definition: TruncSilence.h:135
wxWeakRef< wxWindow > mUIParent
Definition: TruncSilence.h:95
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
wxTextCtrl * mThresholdText
Definition: TruncSilence.h:106
double mInitialAllowedSilence
Definition: TruncSilence.h:99
bool NeedsDither() const override
double CalcPreviewInputLength(const EffectSettings &settings, double previewLength) const override
void OnControlChange(wxCommandEvent &evt)
static constexpr EffectParameter Minimum
Definition: TruncSilence.h:129
bool FindSilences(RegionList &silences, const TrackIterRange< const WaveTrack > &range)
bool Analyze(RegionList &silenceList, RegionList &trackSilences, const WaveTrack &wt, sampleCount *silentFrame, sampleCount *index, int whichTrack, double *inputLength=nullptr, double *minInputLength=nullptr) const
virtual ~EffectTruncSilence()
wxCheckBox * mIndependent
Definition: TruncSilence.h:111
bool TransferDataFromWindow(EffectSettings &settings) override
double mSilenceCompressPercent
Definition: TruncSilence.h:101
static constexpr EffectParameter Truncate
Definition: TruncSilence.h:131
EffectType GetType() const override
Type determines how it behaves.
static const EnumValueSymbol kActionStrings[nActions]
Definition: TruncSilence.h:123
double mTruncLongestAllowedSilence
Definition: TruncSilence.h:100
static constexpr EffectParameter Threshold
Definition: TruncSilence.h:125
wxTextCtrl * mTruncLongestAllowedSilenceT
Definition: TruncSilence.h:109
static const ComponentInterfaceSymbol Symbol
Definition: TruncSilence.h:37
const EffectParameterMethods & Parameters() const override
TranslatableString GetDescription() const override
ComponentInterfaceSymbol GetSymbol() const override
bool LoadSettings(const CommandParameters &parms, EffectSettings &settings) const override
Restore settings from keys and values.
wxTextCtrl * mInitialAllowedSilenceT
Definition: TruncSilence.h:108
wxTextCtrl * mSilenceCompressPercentT
Definition: TruncSilence.h:110
static constexpr EffectParameter Compress
Definition: TruncSilence.h:133
bool DoRemoval(const RegionList &silences, const TrackIterRange< Track > &range, unsigned iGroup, unsigned nGroups, double &totalCutLen)
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[]
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
static TrackIterRange< Track > Group(Track &track)
Definition: SyncLock.cpp:150
static bool IsSelectedOrSyncLockSelectedP(const Track *pTrack)
Definition: SyncLock.h:61
bool IsSyncLocked() const
Definition: SyncLock.cpp:44
static SyncLockState & Get(AudacityProject &project)
Definition: SyncLock.cpp:27
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
bool IsSelected() const
Definition: Track.cpp:258
static auto SingletonRange(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:981
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
auto Channels()
Definition: WaveTrack.h:263
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:1138
double GetEndTime() const override
Implement WideSampleSequence.
Definition: WaveTrack.cpp:2586
double GetRate() const override
Definition: WaveTrack.cpp:798
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:2258
double LongSamplesToTime(sampleCount pos) const
sampleCount TimeToLongSamples(double t0) const
double SnapToSample(double t) const
virtual bool Read(const wxString &key, bool *value) const =0
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
BuiltinEffectsModule::Registration< EffectTruncSilence > reg
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
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.
Range between two TrackIters, usable in range-for statements, and with Visit member functions.
Definition: Track.h:682
Structure to hold region of a wavetrack and a comparison function for sortability.
Definition: WaveTrack.h:217