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{ *mTracks, {{ mT0, mT1 }}, true, true };
277 double newT1 = 0.0;
278
279 {
280 unsigned iGroup = 0;
281 for (auto track : outputs.Get().Selected<WaveTrack>()) {
282 RegionList silences;
283 if (!FindSilences(silences,
284 TrackList::SingletonRange(&as_const(*track))))
285 return false;
286 // Treat tracks in the sync lock group only
287 Track *groupFirst, *groupLast;
288 auto range = syncLock
289 ? SyncLock::Group(track)
290 : TrackList::SingletonRange<Track>(track);
291 double totalCutLen = 0.0;
292 if (!DoRemoval(silences, range, iGroup, nGroups, totalCutLen))
293 return false;
294 newT1 = std::max(newT1, mT1 - totalCutLen);
295
296 ++iGroup;
297 }
298 }
299
300 mT1 = newT1;
301
302 outputs.Commit();
303
304 return true;
305}
306
308{
309 // Copy tracks
310 EffectOutputTracks outputs{ *mTracks, {{ mT0, mT1 }}, true, true };
311
312 // Master list of silent regions.
313 // This list should always be kept in order.
314 RegionList silences;
315
316 if (FindSilences(silences, outputs.Get().Selected<const WaveTrack>()))
317 {
318 auto trackRange = outputs.Get().Any();
319 double totalCutLen = 0.0;
320 if (DoRemoval(silences, trackRange, 0, 1, totalCutLen)) {
321 mT1 -= totalCutLen;
322 outputs.Commit();
323 return true;
324 }
325 }
326
327 return false;
328}
329
332{
333 // Start with the whole selection silent
334 silences.push_back(Region(mT0, mT1));
335
336 // Remove non-silent regions in each track
337 int whichTrack = 0;
338 for (auto wt : range) {
339 assert(wt->IsLeader());
340 // Smallest silent region to detect in frames
341 auto minSilenceFrames =
343 * wt->GetRate());
344
345 //
346 // Scan the track for silences
347 //
348 RegionList trackSilences;
349
350 auto index = wt->TimeToLongSamples(mT0);
351 sampleCount silentFrame = 0;
352
353 // Detect silences
354 bool cancelled = !(Analyze(
355 silences, trackSilences, *wt, &silentFrame, &index, whichTrack));
356
357 // Buffer has been freed, so we're OK to return if cancelled
358 if (cancelled)
359 return false;
360
361 if (silentFrame >= minSilenceFrames)
362 {
363 // Track ended in silence -- record region
364 trackSilences.push_back(Region(
365 wt->LongSamplesToTime(index - silentFrame),
366 wt->LongSamplesToTime(index)
367 ));
368 }
369
370 // Intersect with the overall silent region list
371 Intersect(silences, trackSilences);
372 whichTrack++;
373 }
374
375 return true;
376}
377
379 const TrackIterRange<Track> &range,
380 unsigned iGroup, unsigned nGroups,
381 double &totalCutLen)
382{
383 //
384 // Now remove the silent regions from all selected / sync-lock selected tracks.
385 //
386
387 // Loop over detected regions in reverse (so cuts don't change time values
388 // down the line)
389 int whichReg = 0;
390 RegionList::const_reverse_iterator rit;
391 for (rit = silences.rbegin(); rit != silences.rend(); ++rit)
392 {
393 const Region &region = *rit;
394 const Region *const r = &region;
395
396 // Progress dialog and cancellation. Do additional cleanup before return.
397 const double frac = detectFrac +
398 (1 - detectFrac) * (iGroup + whichReg / double(silences.size())) / nGroups;
399 if (TotalProgress(frac))
400 return false;
401
402 // Intersection may create regions smaller than allowed; ignore them.
403 // Allow one nanosecond extra for consistent results with exact milliseconds of allowed silence.
404 if ((r->end - r->start) < (mInitialAllowedSilence - 0.000000001))
405 continue;
406
407 // Find NEW silence length as requested
408 double inLength = r->end - r->start;
409 double outLength;
410
411 switch (mActionIndex)
412 {
413 case kTruncate:
414 outLength = std::min(mTruncLongestAllowedSilence, inLength);
415 break;
416 case kCompress:
417 outLength = mInitialAllowedSilence +
418 (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0;
419 break;
420 default: // Not currently used.
421 outLength = std::min(mInitialAllowedSilence +
422 (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0,
424 }
425
426 const double cutLen = std::max(0.0, inLength - outLength);
427 // Don't waste time cutting nothing.
428 if( cutLen == 0.0 )
429 continue;
430
431 totalCutLen += cutLen;
432
433 bool success = true;
434 double cutStart = (r->start + r->end - cutLen) / 2;
435 double cutEnd = cutStart + cutLen;
436 (range
438 - [&](const Track *pTrack) { return
439 // Don't waste time past the end of a track
440 pTrack->GetEndTime() < r->start;
441 }
442 ).VisitWhile(success,
443 [&](WaveTrack &wt) {
444 assert(wt.IsLeader());
445 // In WaveTracks, clear with a cross-fade
446 auto blendFrames = mBlendFrameCount;
447 // Round start/end times to frame boundaries
448 cutStart = wt.SnapToSample(cutStart);
449 cutEnd = wt.SnapToSample(cutEnd);
450
451 // Make sure the cross-fade does not affect non-silent frames
452 if (wt.LongSamplesToTime(blendFrames) > inLength) {
453 // Result is not more than blendFrames:
454 blendFrames = wt.TimeToLongSamples(inLength).as_size_t();
455 }
456
457 // Perform cross-fade in memory
458 struct Buffers {
459 Buffers(size_t size)
460 : buf1{ size }, buf2 { size }
461 {}
462 Floats buf1, buf2;
463 };
464 Buffers buffers[2]{ blendFrames, blendFrames };
465 auto t1 = wt.TimeToLongSamples(cutStart) - blendFrames / 2;
466 auto t2 = wt.TimeToLongSamples(cutEnd) - blendFrames / 2;
467
468 size_t iChannel = 0;
469 for (const auto pChannel : wt.Channels()) {
470 auto &buffer = buffers[iChannel];
471 pChannel->GetFloats(buffer.buf1.get(), t1, blendFrames);
472 pChannel->GetFloats(buffer.buf2.get(), t2, blendFrames);
473
474 for (decltype(blendFrames) i = 0; i < blendFrames; ++i) {
475 buffer.buf1[i] =
476 ((blendFrames - i) * buffer.buf1[i] + i * buffer.buf2[i]) /
477 (double)blendFrames;
478 }
479 ++iChannel;
480 }
481
482 assert(wt.IsLeader()); // given range visits leaders only
483 wt.Clear(cutStart, cutEnd);
484
485 iChannel = 0;
486 for (const auto pChannel : wt.Channels()) {
487 // Write cross-faded data
488 auto &buffer = buffers[iChannel];
489 success = success &&
490 pChannel->Set((samplePtr)buffer.buf1.get(), floatSample, t1,
491 blendFrames,
492 // This effect mostly shifts samples to remove silences, and
493 // does only a little bit of floating point calculations to
494 // cross-fade the splices, over a 100 sample interval by default.
495 // Don't dither.
497 ++iChannel;
498 }
499 },
500 [&](Track &t) {
501 assert(t.IsLeader());
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 assert(wt.IsLeader());
520 const auto rate = wt.GetRate();
521
522 // Smallest silent region to detect in frames
523 auto minSilenceFrames =
525
526 double truncDbSilenceThreshold = DB_TO_LINEAR(mThresholdDB);
527 auto blockLen = wt.GetMaxBlockSize();
528 auto start = wt.TimeToLongSamples(mT0);
529 auto end = wt.TimeToLongSamples(mT1);
530 sampleCount outLength = 0;
531
532 double previewLength;
533 gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLength, 6.0);
534 // Minimum required length in samples.
535 const sampleCount previewLen(previewLength * rate);
536
537 // Keep position in overall silences list for optimization
538 RegionList::iterator rit(silenceList.begin());
539
540 // Allocate buffers
541 Floats buffers[] {
542 Floats{ blockLen },
543 Floats{ blockLen }
544 };
545
546 // Loop through current track
547 while (*index < end) {
548 if (inputLength && ((outLength >= previewLen) ||
549 (*index - start > wt.TimeToLongSamples(*minInputLength)))
550 ) {
551 *inputLength = std::min<double>(*inputLength, *minInputLength);
552 if (outLength >= previewLen) {
553 *minInputLength = *inputLength;
554 }
555 return true;
556 }
557
558 if (!inputLength) {
559 // Show progress dialog, test for cancellation
560 bool cancelled = TotalProgress(
561 detectFrac * (whichTrack +
562 (*index - start).as_double() /
563 (end - start).as_double()) /
564 (double)GetNumWaveTracks());
565 if (cancelled)
566 return false;
567 }
568
569 // Optimization: if not in a silent region skip ahead to the next one
570
571 double curTime = wt.LongSamplesToTime(*index);
572 for ( ; rit != silenceList.end(); ++rit) {
573 // Find the first silent region ending after current time
574 if (rit->end >= curTime) {
575 break;
576 }
577 }
578
579 if (rit == silenceList.end()) {
580 // No more regions -- no need to process the rest of the track
581 if (inputLength) {
582 // Add available samples up to previewLength.
583 auto remainingTrackSamples =
584 wt.TimeToLongSamples(wt.GetEndTime()) - *index;
585 auto requiredTrackSamples = previewLen - outLength;
586 outLength += (remainingTrackSamples > requiredTrackSamples)
587 ? requiredTrackSamples
588 : remainingTrackSamples;
589 }
590
591 break;
592 }
593 else if (rit->start > curTime) {
594 // End current silent region, skip ahead
595 if (*silentFrame >= minSilenceFrames) {
596 trackSilences.push_back(Region(
597 wt.LongSamplesToTime(*index - *silentFrame),
598 wt.LongSamplesToTime(*index)
599 ));
600 }
601 *silentFrame = 0;
602 auto newIndex = wt.TimeToLongSamples(rit->start);
603 if (inputLength) {
604 auto requiredTrackSamples = previewLen - outLength;
605 // Add non-silent sample to outLength
606 outLength += ((newIndex - *index) > requiredTrackSamples)
607 ? requiredTrackSamples
608 : newIndex - *index;
609 }
610
611 *index = newIndex;
612 }
613 // End of optimization
614
615 // Limit size of current block if we've reached the end
616 auto count = limitSampleBufferSize( blockLen, end - *index );
617
618 // Fill buffers
619 size_t iChannel = 0;
620 for (const auto pChannel : wt.Channels())
621 pChannel->GetFloats(buffers[iChannel++].get(), *index, count);
622
623 // Look for silenceList in current block
624 for (decltype(count) i = 0; i < count; ++i) {
625 if (inputLength && ((outLength >= previewLen) ||
626 (outLength > wt.TimeToLongSamples(*minInputLength)))
627 ) {
628 *inputLength =
629 wt.LongSamplesToTime(*index + i) - wt.LongSamplesToTime(start);
630 break;
631 }
632
633 const bool silent = std::all_of(buffers, buffers + iChannel,
634 [&](const Floats &buffer){
635 return fabs(buffer[i]) < truncDbSilenceThreshold;
636 });
637 if (silent)
638 (*silentFrame)++;
639 else {
640 sampleCount allowed = 0;
641 if (*silentFrame >= minSilenceFrames) {
642 if (inputLength) {
643 switch (mActionIndex) {
644 case kTruncate:
645 outLength +=
647 break;
648 case kCompress:
649 allowed =
651 outLength += sampleCount(
652 allowed.as_double() +
653 (*silentFrame - allowed).as_double()
655 );
656 break;
657 // default: // Not currently used.
658 }
659 }
660
661 // Record the silent region
662 trackSilences.push_back(Region(
663 wt.LongSamplesToTime(*index + i - *silentFrame),
664 wt.LongSamplesToTime(*index + i)
665 ));
666 }
667 else if (inputLength) { // included as part of non-silence
668 outLength += *silentFrame;
669 }
670 *silentFrame = 0;
671 if (inputLength) {
672 ++outLength; // Add non-silent sample to outLength
673 }
674 }
675 }
676 // Next block
677 *index += count;
678 }
679
680 if (inputLength) {
681 *inputLength = std::min<double>(*inputLength, *minInputLength);
682 if (outLength >= previewLen) {
683 *minInputLength = *inputLength;
684 }
685 }
686
687 return true;
688}
689
690
691std::unique_ptr<EffectEditor> EffectTruncSilence::PopulateOrExchange(
693 const EffectOutputs *)
694{
695 mUIParent = S.GetParent();
696 wxASSERT(nActions == WXSIZEOF(kActionStrings));
697
698 S.AddSpace(0, 5);
699
700 S.StartStatic(XO("Detect Silence"));
701 {
702 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
703 {
704 // Threshold
706 .Validator<FloatingPointValidator<double>>(
707 3, &mThresholdDB, NumValidatorStyle::NO_TRAILING_ZEROES,
709 .NameSuffix(XO("db"))
710 .AddTextBox(XXO("&Threshold:"), wxT(""), 0);
711 S.AddUnits(XO("dB"));
712
713 // Ignored silence
714 mInitialAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
716 NumValidatorStyle::NO_TRAILING_ZEROES,
718 .NameSuffix(XO("seconds"))
719 .AddTextBox(XXO("&Duration:"), wxT(""), 12);
720 S.AddUnits(XO("seconds"));
721 }
722 S.EndMultiColumn();
723 }
724 S.EndStatic();
725
726 S.StartStatic(XO("Action"));
727 {
728 S.StartHorizontalLay();
729 {
730 // Action choices
731 auto actionChoices = Msgids( kActionStrings, nActions );
733 .Validator<wxGenericValidator>(&mActionIndex)
734 .MinSize( { -1, -1 } )
735 .AddChoice( {}, actionChoices );
736 }
737 S.EndHorizontalLay();
738 S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
739 {
740 // Truncation / Compression factor
741
742 mTruncLongestAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
744 NumValidatorStyle::NO_TRAILING_ZEROES,
746 .NameSuffix(XO("seconds"))
747 .AddTextBox(XXO("Tr&uncate to:"), wxT(""), 12);
748 S.AddUnits(XO("seconds"));
749
750 mSilenceCompressPercentT = S.Validator<FloatingPointValidator<double>>(
752 NumValidatorStyle::NO_TRAILING_ZEROES,
754 .NameSuffix(XO("%"))
755 .AddTextBox(XXO("C&ompress to:"), wxT(""), 12);
756 S.AddUnits(XO("%"));
757 }
758 S.EndMultiColumn();
759
760 S.StartMultiColumn(2, wxALIGN_CENTER_HORIZONTAL);
761 {
762 mIndependent = S.AddCheckBox(XXO("Trunc&ate tracks independently"),
764 }
765 S.EndMultiColumn();
766 }
767 S.EndStatic();
768
769 UpdateUI();
770 return nullptr;
771}
772
774{
775 if (!mUIParent->TransferDataToWindow())
776 {
777 return false;
778 }
779
780 return true;
781}
782
784{
785 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
786 {
787 return false;
788 }
789
790 mbIndependent = mIndependent->IsChecked();
791
792 return true;
793}
794
795// EffectTruncSilence implementation
796
797// Finds the intersection of the ordered region lists, stores in dest
799{
800 RegionList::iterator destIter;
801 destIter = dest.begin();
802 // Any time we reach the end of the dest list we're finished
803 if (destIter == dest.end())
804 return;
805 RegionList::iterator curDest = destIter;
806
807 // Operation: find non-silent regions in src, remove them from dest.
808 double nsStart = curDest->start;
809 double nsEnd;
810 bool lastRun = false; // must run the loop one extra time
811
812 RegionList::const_iterator srcIter = src.begin();
813
814 // This logic, causing the loop to run once after end of src, must occur
815 // each time srcIter is updated
816 if (srcIter == src.end())
817 {
818 lastRun = true;
819 }
820
821 while (srcIter != src.end() || lastRun)
822 {
823 // Don't use curSrc unless lastRun is false!
824 RegionList::const_iterator curSrc;
825
826 if (lastRun)
827 {
828 // The last non-silent region extends as far as possible
829 nsEnd = std::numeric_limits<double>::max();
830 }
831 else
832 {
833 curSrc = srcIter;
834 nsEnd = curSrc->start;
835 }
836
837 if (nsEnd > nsStart)
838 {
839 // Increment through dest until we have a region that could be affected
840 while (curDest->end <= nsStart)
841 {
842 ++destIter;
843 if (destIter == dest.end())
844 {
845 return;
846 }
847 curDest = destIter;
848 }
849
850 // Check for splitting dest region in two
851 if (nsStart > curDest->start && nsEnd < curDest->end)
852 {
853 // The second region
854 Region r(nsEnd, curDest->end);
855
856 // The first region
857 curDest->end = nsStart;
858
859 // Insert second region after first
860 RegionList::iterator nextIt(destIter);
861 ++nextIt;
862
863 // This should just read: destIter = dest.insert(nextIt, r); but we
864 // work around two two wxList::insert() bugs. First, in some
865 // versions it returns the wrong value. Second, in some versions,
866 // it crashes when you insert at list end.
867 if (nextIt == dest.end())
868 dest.push_back(r);
869 else
870 dest.insert(nextIt, r);
871 ++destIter; // (now points at the newly-inserted region)
872
873 curDest = destIter;
874 }
875
876 // Check for truncating the end of dest region
877 if (nsStart > curDest->start && nsStart < curDest->end &&
878 nsEnd >= curDest->end)
879 {
880 curDest->end = nsStart;
881
882 ++destIter;
883 if (destIter == dest.end())
884 {
885 return;
886 }
887 curDest = destIter;
888 }
889
890 // Check for all dest regions that need to be removed completely
891 while (nsStart <= curDest->start && nsEnd >= curDest->end)
892 {
893 destIter = dest.erase(destIter);
894 if (destIter == dest.end())
895 {
896 return;
897 }
898 curDest = destIter;
899 }
900
901 // Check for truncating the beginning of dest region
902 if (nsStart <= curDest->start &&
903 nsEnd > curDest->start && nsEnd < curDest->end)
904 {
905 curDest->start = nsEnd;
906 }
907 }
908
909 if (lastRun)
910 {
911 // done
912 lastRun = false;
913 }
914 else
915 {
916 // Next non-silent region starts at the end of this silent region
917 nsStart = curSrc->end;
918 ++srcIter;
919 if (srcIter == src.end())
920 {
921 lastRun = true;
922 }
923 }
924 }
925}
926
927/*
928void EffectTruncSilence::BlendFrames(float* buffer, int blendFrameCount, int leftIndex, int rightIndex)
929{
930 float* bufOutput = &buffer[leftIndex];
931 float* bufBefore = &buffer[leftIndex];
932 float* bufAfter = &buffer[rightIndex];
933 double beforeFactor = 1.0;
934 double afterFactor = 0.0;
935 double adjFactor = 1.0 / (double)blendFrameCount;
936 for (int j = 0; j < blendFrameCount; ++j)
937 {
938 bufOutput[j] = (float)((bufBefore[j] * beforeFactor) + (bufAfter[j] * afterFactor));
939 beforeFactor -= adjFactor;
940 afterFactor += adjFactor;
941 }
942}
943*/
944
946{
947 switch (mActionIndex)
948 {
949 case kTruncate:
950 mTruncLongestAllowedSilenceT->Enable(true);
951 mSilenceCompressPercentT->Enable(false);
952 break;
953 case kCompress:
954 mTruncLongestAllowedSilenceT->Enable(false);
955 mSilenceCompressPercentT->Enable(true);
956 }
957}
958
959void EffectTruncSilence::OnControlChange(wxCommandEvent & WXUNUSED(evt))
960{
961 mActionChoice->GetValidator()->TransferFromWindow();
962
963 UpdateUI();
964
966 mUIParent, mUIParent->TransferDataFromWindow()))
967 {
968 return;
969 }
970}
971
973{
974 return false;
975}
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:561
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:55
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Convenience function often useful when adding choice controls.
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:83
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:224
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:129
bool TotalProgress(double frac, const TranslatableString &={}) const
Definition: Effect.cpp:341
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:123
bool IsSelected() const
Definition: Track.cpp:295
static auto SingletonRange(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1122
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
Definition: WaveTrack.h:220
auto Channels()
Definition: WaveTrack.h:268
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:1215
bool IsLeader() const override
Definition: WaveTrack.cpp:2443
double GetEndTime() const override
Implement WideSampleSequence.
Definition: WaveTrack.cpp:2717
double GetRate() const override
Definition: WaveTrack.cpp:868
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:2390
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:816
Structure to hold region of a wavetrack and a comparison function for sortability.
Definition: WaveTrack.h:228