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 assert(wt->IsLeader());
344 // Smallest silent region to detect in frames
345 auto minSilenceFrames =
347 * wt->GetRate());
348
349 //
350 // Scan the track for silences
351 //
352 RegionList trackSilences;
353
354 auto index = wt->TimeToLongSamples(mT0);
355 sampleCount silentFrame = 0;
356
357 // Detect silences
358 bool cancelled = !(Analyze(
359 silences, trackSilences, *wt, &silentFrame, &index, whichTrack));
360
361 // Buffer has been freed, so we're OK to return if cancelled
362 if (cancelled)
363 return false;
364
365 if (silentFrame >= minSilenceFrames)
366 {
367 // Track ended in silence -- record region
368 trackSilences.push_back(Region(
369 wt->LongSamplesToTime(index - silentFrame),
370 wt->LongSamplesToTime(index)
371 ));
372 }
373
374 // Intersect with the overall silent region list
375 Intersect(silences, trackSilences);
376 whichTrack++;
377 }
378
379 return true;
380}
381
383 const TrackIterRange<Track> &range,
384 unsigned iGroup, unsigned nGroups,
385 double &totalCutLen)
386{
387 //
388 // Now remove the silent regions from all selected / sync-lock selected tracks.
389 //
390
391 // Loop over detected regions in reverse (so cuts don't change time values
392 // down the line)
393 int whichReg = 0;
394 RegionList::const_reverse_iterator rit;
395 for (rit = silences.rbegin(); rit != silences.rend(); ++rit)
396 {
397 const Region &region = *rit;
398 const Region *const r = &region;
399
400 // Progress dialog and cancellation. Do additional cleanup before return.
401 const double frac = detectFrac +
402 (1 - detectFrac) * (iGroup + whichReg / double(silences.size())) / nGroups;
403 if (TotalProgress(frac))
404 return false;
405
406 // Intersection may create regions smaller than allowed; ignore them.
407 // Allow one nanosecond extra for consistent results with exact milliseconds of allowed silence.
408 if ((r->end - r->start) < (mInitialAllowedSilence - 0.000000001))
409 continue;
410
411 // Find NEW silence length as requested
412 double inLength = r->end - r->start;
413 double outLength;
414
415 switch (mActionIndex)
416 {
417 case kTruncate:
418 outLength = std::min(mTruncLongestAllowedSilence, inLength);
419 break;
420 case kCompress:
421 outLength = mInitialAllowedSilence +
422 (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0;
423 break;
424 default: // Not currently used.
425 outLength = std::min(mInitialAllowedSilence +
426 (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0,
428 }
429
430 const double cutLen = std::max(0.0, inLength - outLength);
431 // Don't waste time cutting nothing.
432 if( cutLen == 0.0 )
433 continue;
434
435 totalCutLen += cutLen;
436
437 bool success = true;
438 double cutStart = (r->start + r->end - cutLen) / 2;
439 double cutEnd = cutStart + cutLen;
440 (range
442 - [&](const Track *pTrack) { return
443 // Don't waste time past the end of a track
444 pTrack->GetEndTime() < r->start;
445 }
446 ).VisitWhile(success,
447 [&](WaveTrack &wt) {
448 assert(wt.IsLeader());
449 // In WaveTracks, clear with a cross-fade
450 auto blendFrames = mBlendFrameCount;
451 // Round start/end times to frame boundaries
452 cutStart = wt.SnapToSample(cutStart);
453 cutEnd = wt.SnapToSample(cutEnd);
454
455 // Make sure the cross-fade does not affect non-silent frames
456 if (wt.LongSamplesToTime(blendFrames) > inLength) {
457 // Result is not more than blendFrames:
458 blendFrames = wt.TimeToLongSamples(inLength).as_size_t();
459 }
460
461 // Perform cross-fade in memory
462 struct Buffers {
463 Buffers(size_t size)
464 : buf1{ size }, buf2 { size }
465 {}
466 Floats buf1, buf2;
467 };
468 Buffers buffers[2]{ blendFrames, blendFrames };
469 auto t1 = wt.TimeToLongSamples(cutStart) - blendFrames / 2;
470 auto t2 = wt.TimeToLongSamples(cutEnd) - blendFrames / 2;
471
472 size_t iChannel = 0;
473 for (const auto pChannel : wt.Channels()) {
474 auto &buffer = buffers[iChannel];
475 pChannel->GetFloats(buffer.buf1.get(), t1, blendFrames);
476 pChannel->GetFloats(buffer.buf2.get(), t2, blendFrames);
477
478 for (decltype(blendFrames) i = 0; i < blendFrames; ++i) {
479 buffer.buf1[i] =
480 ((blendFrames - i) * buffer.buf1[i] + i * buffer.buf2[i]) /
481 (double)blendFrames;
482 }
483 ++iChannel;
484 }
485
486 assert(wt.IsLeader()); // given range visits leaders only
487 wt.Clear(cutStart, cutEnd);
488
489 iChannel = 0;
490 for (const auto pChannel : wt.Channels()) {
491 // Write cross-faded data
492 auto &buffer = buffers[iChannel];
493 success = success &&
494 pChannel->Set((samplePtr)buffer.buf1.get(), floatSample, t1,
495 blendFrames,
496 // This effect mostly shifts samples to remove silences, and
497 // does only a little bit of floating point calculations to
498 // cross-fade the splices, over a 100 sample interval by default.
499 // Don't dither.
501 ++iChannel;
502 }
503 },
504 [&](Track &t) {
505 assert(t.IsLeader());
506 // Non-wave tracks: just do a sync-lock adjust
507 t.SyncLockAdjust(cutEnd, cutStart);
508 }
509 );
510 if (!success)
511 return false;
512 ++whichReg;
513 }
514
515 return true;
516}
517
519 RegionList& trackSilences, const WaveTrack &wt, sampleCount* silentFrame,
520 sampleCount* index, int whichTrack, double* inputLength,
521 double* minInputLength) const
522{
523 assert(wt.IsLeader());
524 const auto rate = wt.GetRate();
525
526 // Smallest silent region to detect in frames
527 auto minSilenceFrames =
529
530 double truncDbSilenceThreshold = DB_TO_LINEAR(mThresholdDB);
531 auto blockLen = wt.GetMaxBlockSize();
532 auto start = wt.TimeToLongSamples(mT0);
533 auto end = wt.TimeToLongSamples(mT1);
534 sampleCount outLength = 0;
535
536 double previewLength;
537 gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLength, 6.0);
538 // Minimum required length in samples.
539 const sampleCount previewLen(previewLength * rate);
540
541 // Keep position in overall silences list for optimization
542 RegionList::iterator rit(silenceList.begin());
543
544 // Allocate buffers
545 Floats buffers[] {
546 Floats{ blockLen },
547 Floats{ blockLen }
548 };
549
550 // Loop through current track
551 while (*index < end) {
552 if (inputLength && ((outLength >= previewLen) ||
553 (*index - start > wt.TimeToLongSamples(*minInputLength)))
554 ) {
555 *inputLength = std::min<double>(*inputLength, *minInputLength);
556 if (outLength >= previewLen) {
557 *minInputLength = *inputLength;
558 }
559 return true;
560 }
561
562 if (!inputLength) {
563 // Show progress dialog, test for cancellation
564 bool cancelled = TotalProgress(
565 detectFrac * (whichTrack +
566 (*index - start).as_double() /
567 (end - start).as_double()) /
568 (double)GetNumWaveTracks());
569 if (cancelled)
570 return false;
571 }
572
573 // Optimization: if not in a silent region skip ahead to the next one
574
575 double curTime = wt.LongSamplesToTime(*index);
576 for ( ; rit != silenceList.end(); ++rit) {
577 // Find the first silent region ending after current time
578 if (rit->end >= curTime) {
579 break;
580 }
581 }
582
583 if (rit == silenceList.end()) {
584 // No more regions -- no need to process the rest of the track
585 if (inputLength) {
586 // Add available samples up to previewLength.
587 auto remainingTrackSamples =
588 wt.TimeToLongSamples(wt.GetEndTime()) - *index;
589 auto requiredTrackSamples = previewLen - outLength;
590 outLength += (remainingTrackSamples > requiredTrackSamples)
591 ? requiredTrackSamples
592 : remainingTrackSamples;
593 }
594
595 break;
596 }
597 else if (rit->start > curTime) {
598 // End current silent region, skip ahead
599 if (*silentFrame >= minSilenceFrames) {
600 trackSilences.push_back(Region(
601 wt.LongSamplesToTime(*index - *silentFrame),
602 wt.LongSamplesToTime(*index)
603 ));
604 }
605 *silentFrame = 0;
606 auto newIndex = wt.TimeToLongSamples(rit->start);
607 if (inputLength) {
608 auto requiredTrackSamples = previewLen - outLength;
609 // Add non-silent sample to outLength
610 outLength += ((newIndex - *index) > requiredTrackSamples)
611 ? requiredTrackSamples
612 : newIndex - *index;
613 }
614
615 *index = newIndex;
616 }
617 // End of optimization
618
619 // Limit size of current block if we've reached the end
620 auto count = limitSampleBufferSize( blockLen, end - *index );
621
622 // Fill buffers
623 size_t iChannel = 0;
624 for (const auto pChannel : wt.Channels())
625 pChannel->GetFloats(buffers[iChannel++].get(), *index, count);
626
627 // Look for silenceList in current block
628 for (decltype(count) i = 0; i < count; ++i) {
629 if (inputLength && ((outLength >= previewLen) ||
630 (outLength > wt.TimeToLongSamples(*minInputLength)))
631 ) {
632 *inputLength =
633 wt.LongSamplesToTime(*index + i) - wt.LongSamplesToTime(start);
634 break;
635 }
636
637 const bool silent = std::all_of(buffers, buffers + iChannel,
638 [&](const Floats &buffer){
639 return fabs(buffer[i]) < truncDbSilenceThreshold;
640 });
641 if (silent)
642 (*silentFrame)++;
643 else {
644 sampleCount allowed = 0;
645 if (*silentFrame >= minSilenceFrames) {
646 if (inputLength) {
647 switch (mActionIndex) {
648 case kTruncate:
649 outLength +=
651 break;
652 case kCompress:
653 allowed =
655 outLength += sampleCount(
656 allowed.as_double() +
657 (*silentFrame - allowed).as_double()
659 );
660 break;
661 // default: // Not currently used.
662 }
663 }
664
665 // Record the silent region
666 trackSilences.push_back(Region(
667 wt.LongSamplesToTime(*index + i - *silentFrame),
668 wt.LongSamplesToTime(*index + i)
669 ));
670 }
671 else if (inputLength) { // included as part of non-silence
672 outLength += *silentFrame;
673 }
674 *silentFrame = 0;
675 if (inputLength) {
676 ++outLength; // Add non-silent sample to outLength
677 }
678 }
679 }
680 // Next block
681 *index += count;
682 }
683
684 if (inputLength) {
685 *inputLength = std::min<double>(*inputLength, *minInputLength);
686 if (outLength >= previewLen) {
687 *minInputLength = *inputLength;
688 }
689 }
690
691 return true;
692}
693
694
695std::unique_ptr<EffectEditor> EffectTruncSilence::PopulateOrExchange(
697 const EffectOutputs *)
698{
699 mUIParent = S.GetParent();
700 wxASSERT(nActions == WXSIZEOF(kActionStrings));
701
702 S.AddSpace(0, 5);
703
704 S.StartStatic(XO("Detect Silence"));
705 {
706 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
707 {
708 // Threshold
710 .Validator<FloatingPointValidator<double>>(
711 3, &mThresholdDB, NumValidatorStyle::NO_TRAILING_ZEROES,
713 .NameSuffix(XO("db"))
714 .AddTextBox(XXO("&Threshold:"), wxT(""), 0);
715 S.AddUnits(XO("dB"));
716
717 // Ignored silence
718 mInitialAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
720 NumValidatorStyle::NO_TRAILING_ZEROES,
722 .NameSuffix(XO("seconds"))
723 .AddTextBox(XXO("&Duration:"), wxT(""), 12);
724 S.AddUnits(XO("seconds"));
725 }
726 S.EndMultiColumn();
727 }
728 S.EndStatic();
729
730 S.StartStatic(XO("Action"));
731 {
732 S.StartHorizontalLay();
733 {
734 // Action choices
735 auto actionChoices = Msgids( kActionStrings, nActions );
737 .Validator<wxGenericValidator>(&mActionIndex)
738 .MinSize( { -1, -1 } )
739 .AddChoice( {}, actionChoices );
740 }
741 S.EndHorizontalLay();
742 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
743 {
744 // Truncation / Compression factor
745
746 mTruncLongestAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
748 NumValidatorStyle::NO_TRAILING_ZEROES,
750 .NameSuffix(XO("seconds"))
751 .AddTextBox(XXO("Tr&uncate to:"), wxT(""), 12);
752 S.AddUnits(XO("seconds"));
753
754 mSilenceCompressPercentT = S.Validator<FloatingPointValidator<double>>(
756 NumValidatorStyle::NO_TRAILING_ZEROES,
758 .NameSuffix(XO("%"))
759 .AddTextBox(XXO("C&ompress to:"), wxT(""), 12);
760 S.AddUnits(XO("%"));
761 }
762 S.EndMultiColumn();
763
764 S.StartMultiColumn(2, wxALIGN_CENTER_HORIZONTAL);
765 {
766 mIndependent = S.AddCheckBox(XXO("Trunc&ate tracks independently"),
768 }
769 S.EndMultiColumn();
770 }
771 S.EndStatic();
772
773 UpdateUI();
774 return nullptr;
775}
776
778{
779 if (!mUIParent->TransferDataToWindow())
780 {
781 return false;
782 }
783
784 return true;
785}
786
788{
789 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
790 {
791 return false;
792 }
793
794 mbIndependent = mIndependent->IsChecked();
795
796 return true;
797}
798
799// EffectTruncSilence implementation
800
801// Finds the intersection of the ordered region lists, stores in dest
803{
804 RegionList::iterator destIter;
805 destIter = dest.begin();
806 // Any time we reach the end of the dest list we're finished
807 if (destIter == dest.end())
808 return;
809 RegionList::iterator curDest = destIter;
810
811 // Operation: find non-silent regions in src, remove them from dest.
812 double nsStart = curDest->start;
813 double nsEnd;
814 bool lastRun = false; // must run the loop one extra time
815
816 RegionList::const_iterator srcIter = src.begin();
817
818 // This logic, causing the loop to run once after end of src, must occur
819 // each time srcIter is updated
820 if (srcIter == src.end())
821 {
822 lastRun = true;
823 }
824
825 while (srcIter != src.end() || lastRun)
826 {
827 // Don't use curSrc unless lastRun is false!
828 RegionList::const_iterator curSrc;
829
830 if (lastRun)
831 {
832 // The last non-silent region extends as far as possible
833 nsEnd = std::numeric_limits<double>::max();
834 }
835 else
836 {
837 curSrc = srcIter;
838 nsEnd = curSrc->start;
839 }
840
841 if (nsEnd > nsStart)
842 {
843 // Increment through dest until we have a region that could be affected
844 while (curDest->end <= nsStart)
845 {
846 ++destIter;
847 if (destIter == dest.end())
848 {
849 return;
850 }
851 curDest = destIter;
852 }
853
854 // Check for splitting dest region in two
855 if (nsStart > curDest->start && nsEnd < curDest->end)
856 {
857 // The second region
858 Region r(nsEnd, curDest->end);
859
860 // The first region
861 curDest->end = nsStart;
862
863 // Insert second region after first
864 RegionList::iterator nextIt(destIter);
865 ++nextIt;
866
867 // This should just read: destIter = dest.insert(nextIt, r); but we
868 // work around two two wxList::insert() bugs. First, in some
869 // versions it returns the wrong value. Second, in some versions,
870 // it crashes when you insert at list end.
871 if (nextIt == dest.end())
872 dest.push_back(r);
873 else
874 dest.insert(nextIt, r);
875 ++destIter; // (now points at the newly-inserted region)
876
877 curDest = destIter;
878 }
879
880 // Check for truncating the end of dest region
881 if (nsStart > curDest->start && nsStart < curDest->end &&
882 nsEnd >= curDest->end)
883 {
884 curDest->end = nsStart;
885
886 ++destIter;
887 if (destIter == dest.end())
888 {
889 return;
890 }
891 curDest = destIter;
892 }
893
894 // Check for all dest regions that need to be removed completely
895 while (nsStart <= curDest->start && nsEnd >= curDest->end)
896 {
897 destIter = dest.erase(destIter);
898 if (destIter == dest.end())
899 {
900 return;
901 }
902 curDest = destIter;
903 }
904
905 // Check for truncating the beginning of dest region
906 if (nsStart <= curDest->start &&
907 nsEnd > curDest->start && nsEnd < curDest->end)
908 {
909 curDest->start = nsEnd;
910 }
911 }
912
913 if (lastRun)
914 {
915 // done
916 lastRun = false;
917 }
918 else
919 {
920 // Next non-silent region starts at the end of this silent region
921 nsStart = curSrc->end;
922 ++srcIter;
923 if (srcIter == src.end())
924 {
925 lastRun = true;
926 }
927 }
928 }
929}
930
931/*
932void EffectTruncSilence::BlendFrames(float* buffer, int blendFrameCount, int leftIndex, int rightIndex)
933{
934 float* bufOutput = &buffer[leftIndex];
935 float* bufBefore = &buffer[leftIndex];
936 float* bufAfter = &buffer[rightIndex];
937 double beforeFactor = 1.0;
938 double afterFactor = 0.0;
939 double adjFactor = 1.0 / (double)blendFrameCount;
940 for (int j = 0; j < blendFrameCount; ++j)
941 {
942 bufOutput[j] = (float)((bufBefore[j] * beforeFactor) + (bufAfter[j] * afterFactor));
943 beforeFactor -= adjFactor;
944 afterFactor += adjFactor;
945 }
946}
947*/
948
950{
951 switch (mActionIndex)
952 {
953 case kTruncate:
954 mTruncLongestAllowedSilenceT->Enable(true);
955 mSilenceCompressPercentT->Enable(false);
956 break;
957 case kCompress:
958 mTruncLongestAllowedSilenceT->Enable(false);
959 mSilenceCompressPercentT->Enable(true);
960 }
961}
962
963void EffectTruncSilence::OnControlChange(wxCommandEvent & WXUNUSED(evt))
964{
965 mActionChoice->GetValidator()->TransferFromWindow();
966
967 UpdateUI();
968
970 mUIParent, mUIParent->TransferDataFromWindow()))
971 {
972 return;
973 }
974}
975
977{
978 return false;
979}
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:335
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.
char * samplePtr
Definition: SampleFormat.h:57
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:69
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:116
const TrackList * inputTracks() const
Definition: EffectBase.h:91
std::shared_ptr< TrackList > mTracks
Definition: EffectBase.h:109
double mT0
Definition: EffectBase.h:115
const AudacityProject * FindProject() const
Definition: EffectBase.cpp:225
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:116
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:136
static constexpr EffectParameter Independent
Definition: TruncSilence.h:144
wxWeakRef< wxWindow > mUIParent
Definition: TruncSilence.h:104
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
wxTextCtrl * mThresholdText
Definition: TruncSilence.h:115
double mInitialAllowedSilence
Definition: TruncSilence.h:108
bool NeedsDither() const override
double CalcPreviewInputLength(const EffectSettings &settings, double previewLength) const override
void OnControlChange(wxCommandEvent &evt)
static constexpr EffectParameter Minimum
Definition: TruncSilence.h:138
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:120
bool TransferDataFromWindow(EffectSettings &settings) override
double mSilenceCompressPercent
Definition: TruncSilence.h:110
static constexpr EffectParameter Truncate
Definition: TruncSilence.h:140
EffectType GetType() const override
Type determines how it behaves.
static const EnumValueSymbol kActionStrings[nActions]
Definition: TruncSilence.h:132
double mTruncLongestAllowedSilence
Definition: TruncSilence.h:109
static constexpr EffectParameter Threshold
Definition: TruncSilence.h:134
wxTextCtrl * mTruncLongestAllowedSilenceT
Definition: TruncSilence.h:118
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:117
wxTextCtrl * mSilenceCompressPercentT
Definition: TruncSilence.h:119
static constexpr EffectParameter Compress
Definition: TruncSilence.h:142
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:630
static bool IsSelectedOrSyncLockSelected(const Track *pTrack)
Definition: SyncLock.cpp:112
static TrackIterRange< Track > Group(Track *pTrack)
Definition: SyncLock.cpp:161
bool IsSyncLocked() const
Definition: SyncLock.cpp:43
static SyncLockState & Get(AudacityProject &project)
Definition: SyncLock.cpp:26
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:122
bool IsSelected() const
Definition: Track.cpp:288
static auto SingletonRange(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1128
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
Definition: WaveTrack.h:227
auto Channels()
Definition: WaveTrack.h:277
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:1479
bool IsLeader() const override
Definition: WaveTrack.cpp:2820
double GetEndTime() const override
Implement WideSampleSequence.
Definition: WaveTrack.cpp:3092
double GetRate() const override
Definition: WaveTrack.cpp:1128
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:2741
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
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
BuiltinEffectsModule::Registration< EffectTruncSilence > reg
const wxChar *const key
Identifier in configuration file.
const Type def
Default value.
const Type min
Minimum value.
const Type max
Maximum value.
Externalized state of a plug-in.
Range between two TrackIters, usable in range-for statements, and with Visit member functions.
Definition: Track.h:825
Structure to hold region of a wavetrack and a comparison function for sortability.
Definition: WaveTrack.h:235