Audacity  3.0.3
TruncSilence.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  TruncSilence.cpp
6 
7  Lynn Allan (from DM's Normalize)
8  Philip Van Baren (more options and boundary fixes)
9 
10 *******************************************************************//*******************************************************************/
17 
18 
19 #include "TruncSilence.h"
20 #include "LoadEffects.h"
21 
22 #include <algorithm>
23 #include <list>
24 #include <limits>
25 #include <math.h>
26 
27 #include <wx/checkbox.h>
28 #include <wx/choice.h>
29 #include <wx/valgen.h>
30 
31 #include "Prefs.h"
32 #include "../Project.h"
33 #include "../ProjectSettings.h"
34 #include "../Shuttle.h"
35 #include "../ShuttleGui.h"
36 #include "../WaveTrack.h"
37 #include "../widgets/valnum.h"
38 #include "../widgets/AudacityMessageBox.h"
39 
40 class Enums {
41 public:
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.
66 static inline double enumToDB( int val ) { return -( 5.0 * val + 20.0 ); }
67 
68 const size_t Enums::NumDbChoices = WXSIZEOF(Enums::DbChoices);
69 
70 // Declaration of RegionList
71 class RegionList : public std::list < Region > {};
72 
74 {
77  nActions
78 };
79 
81 {
82  { XO("Truncate Detected Silence") },
83  { XO("Compress Excess Silence") }
84 };
85 
87  // Compatible with 2.1.0 and before
88  { wxT("0"), 0 }, // Remap to Truncate Detected Silence
89  { wxT("1"), 1 }, // Remap to Compress Excess Silence
90 };
91 
92 static const size_t nObsoleteActions = WXSIZEOF( kObsoleteActions );
93 
94 // Define defaults, minimums, and maximums for each parameter
95 #define DefaultAndLimits(name, def, min, max) \
96  static const double DEF_ ## name = (def); \
97  static const double MIN_ ## name = (min); \
98  static const double MAX_ ## name = (max);
99 
100 // Define keys, defaults, minimums, and maximums for the effect parameters
101 //
102 // Name Type Key Def Min Max Scale
103 
104 // This one is legacy and is intentionally not reported by DefineParams:
105 Param( DbIndex, int, wxT("Db"), 0, 0, Enums::NumDbChoices - 1, 1 );
106 
107 Param( Threshold, double, wxT("Threshold"), -20.0, -80.0, -20.0, 1 );
108 Param( ActIndex, int, wxT("Action"), kTruncate, 0, nActions - 1, 1 );
109 Param( Minimum, double, wxT("Minimum"), 0.5, 0.001, 10000.0, 1 );
110 Param( Truncate, double, wxT("Truncate"), 0.5, 0.0, 10000.0, 1 );
111 Param( Compress, double, wxT("Compress"), 50.0, 0.0, 99.9, 1 );
112 Param( Independent, bool, wxT("Independent"), false, false, true, 1 );
113 
114 static const size_t DEF_BlendFrameCount = 100;
115 
116 // Lower bound on the amount of silence to find at a time -- this avoids
117 // detecting silence repeatedly in low-frequency sounds.
118 static const double DEF_MinTruncMs = 0.001;
119 
120 // Typical fraction of total time taken by detection (better to guess low)
121 const double detectFrac = 0.4;
122 
124 { XO("Truncate Silence") };
125 
127 
128 BEGIN_EVENT_TABLE(EffectTruncSilence, wxEvtHandler)
129  EVT_CHOICE(wxID_ANY, EffectTruncSilence::OnControlChange)
130  EVT_TEXT(wxID_ANY, EffectTruncSilence::OnControlChange)
132 
134 {
135  mInitialAllowedSilence = DEF_Minimum;
136  mTruncLongestAllowedSilence = DEF_Truncate;
137  mSilenceCompressPercent = DEF_Compress;
138  mThresholdDB = DEF_Threshold;
139  mActionIndex = DEF_ActIndex;
140  mbIndependent = DEF_Independent;
141 
142  SetLinearEffectFlag(false);
143 
144  // This used to be changeable via the audacity.cfg/registry. Doubtful that was
145  // ever done.
146  //
147  // Original comment:
148  //
149  // mBlendFrameCount only retrieved from prefs ... not using dialog
150  // Only way to change (for windows) is thru registry
151  // The values should be figured dynamically ... too many frames could be invalid
152  mBlendFrameCount = DEF_BlendFrameCount;
153 }
154 
156 {
157 }
158 
159 // ComponentInterface implementation
160 
162 {
163  return Symbol;
164 }
165 
167 {
168  return XO("Automatically reduces the length of passages where the volume is below a specified level");
169 }
170 
172 {
173  return L"Truncate_Silence";
174 }
175 
176 // EffectDefinitionInterface implementation
177 
179 {
180  return EffectTypeProcess;
181 }
182 
183 // EffectClientInterface implementation
184 
186  S.SHUTTLE_PARAM( mThresholdDB, Threshold );
187  S.SHUTTLE_ENUM_PARAM( mActionIndex, ActIndex, kActionStrings, nActions );
188  S.SHUTTLE_PARAM( mInitialAllowedSilence, Minimum );
189  S.SHUTTLE_PARAM( mTruncLongestAllowedSilence, Truncate );
190  S.SHUTTLE_PARAM( mSilenceCompressPercent, Compress );
191  S.SHUTTLE_PARAM( mbIndependent, Independent );
192  return true;
193 }
194 
196 {
197  parms.Write(KEY_Threshold, mThresholdDB);
198  parms.Write(KEY_ActIndex, kActionStrings[mActionIndex].Internal());
199  parms.Write(KEY_Minimum, mInitialAllowedSilence);
200  parms.Write(KEY_Truncate, mTruncLongestAllowedSilence);
201  parms.Write(KEY_Compress, mSilenceCompressPercent);
202  parms.Write(KEY_Independent, mbIndependent);
203 
204  return true;
205 }
206 
208 {
209  ReadAndVerifyDouble(Minimum);
210  ReadAndVerifyDouble(Truncate);
211  ReadAndVerifyDouble(Compress);
212 
213  // This control migrated from a choice to a text box in version 2.3.0
214  double myThreshold {};
215  bool newParams = [&] {
216  ReadAndVerifyDouble(Threshold); // macro may return false
217  myThreshold = Threshold;
218  return true;
219  } ();
220 
221  if ( !newParams ) {
222  // Use legacy param:
224  myThreshold = enumToDB( DbIndex );
225  }
226 
229  ReadAndVerifyBool(Independent);
230 
231  mInitialAllowedSilence = Minimum;
232  mTruncLongestAllowedSilence = Truncate;
233  mSilenceCompressPercent = Compress;
234  mThresholdDB = myThreshold;
235  mActionIndex = ActIndex;
236  mbIndependent = Independent;
237 
238  return true;
239 }
240 
241 // Effect implementation
242 
243 double EffectTruncSilence::CalcPreviewInputLength(double /* previewLength */)
244 {
245  double inputLength = mT1 - mT0;
246  double minInputLength = inputLength;
247 
248  // Master list of silent regions
249  RegionList silences;
250 
251  // Start with the whole selection silent
252  silences.push_back(Region(mT0, mT1));
253 
254  int whichTrack = 0;
255 
256  for (auto wt : inputTracks()->Selected< const WaveTrack >()) {
257  RegionList trackSilences;
258 
259  auto index = wt->TimeToLongSamples(mT0);
260  sampleCount silentFrame = 0; // length of the current silence
261 
262  Analyze(silences, trackSilences, wt, &silentFrame, &index, whichTrack, &inputLength, &minInputLength);
263 
264  whichTrack++;
265  }
266  return inputLength;
267 }
268 
269 
271 {
272  wxString base = wxT("/Effects/TruncateSilence/");
273 
274  // Migrate settings from 2.1.0 or before
275 
276  // Already migrated, so bail
277  if (gPrefs->Exists(base + wxT("Migrated")))
278  {
279  return true;
280  }
281 
282  // Load the old "current" settings
283  if (gPrefs->Exists(base))
284  {
285  int truncDbChoiceIndex = gPrefs->Read(base + wxT("DbChoiceIndex"), 4L);
286  if ((truncDbChoiceIndex < 0) || (truncDbChoiceIndex >= Enums::NumDbChoices))
287  { // corrupted Prefs?
288  truncDbChoiceIndex = 4L;
289  }
290  mThresholdDB = enumToDB( truncDbChoiceIndex );
291  mActionIndex = gPrefs->Read(base + wxT("ProcessChoice"), 0L);
292  if ((mActionIndex < 0) || (mActionIndex > 1))
293  { // corrupted Prefs?
294  mActionIndex = 0L;
295  }
296  gPrefs->Read(base + wxT("InitialAllowedSilence"), &mInitialAllowedSilence, 0.5);
297  if ((mInitialAllowedSilence < 0.001) || (mInitialAllowedSilence > 10000.0))
298  { // corrupted Prefs?
300  }
301  gPrefs->Read(base + wxT("LongestAllowedSilence"), &mTruncLongestAllowedSilence, 0.5);
302  if ((mTruncLongestAllowedSilence < 0.0) || (mTruncLongestAllowedSilence > 10000.0))
303  { // corrupted Prefs?
305  }
306  gPrefs->Read(base + wxT("CompressPercent"), &mSilenceCompressPercent, 50.0);
307  if ((mSilenceCompressPercent < 0.0) || (mSilenceCompressPercent > 100.0))
308  { // corrupted Prefs?
310  }
311 
313  }
314 
315  // Do not migrate again
316  gPrefs->Write(base + wxT("Migrated"), true);
317 
318  return true;
319 }
320 
322 {
323  const bool success =
326  : ProcessAll();
327 
328  if (success)
330 
331  return success;
332 }
333 
335 {
336  unsigned nGroups = 0;
337 
338  const auto &settings = ProjectSettings::Get( *FindProject() );
339  const bool syncLock = settings.IsSyncLocked();
340 
341  // Check if it's permissible
342  {
343  for (auto track : inputTracks()->SelectedLeaders< const WaveTrack >() ) {
344  if (syncLock) {
345  auto channels = TrackList::Channels(track);
346  auto otherTracks =
347  TrackList::SyncLockGroup(track).Filter<const WaveTrack>()
349  - [&](const Track *pTrack){
350  return channels.contains(pTrack); };
351  if (otherTracks) {
353  XO(
354 "When truncating independently, there may only be one selected audio track in each Sync-Locked Track Group.") );
355  return false;
356  }
357  }
358 
359  ++nGroups;
360  }
361  }
362 
363  if (nGroups == 0)
364  // nothing to do
365  return true;
366 
367  // Now do the work
368 
369  // Copy tracks
370  CopyInputTracks(true);
371  double newT1 = 0.0;
372 
373  {
374  unsigned iGroup = 0;
375  for (auto track : mOutputTracks->SelectedLeaders< WaveTrack >() ) {
376  Track *const last = *TrackList::Channels(track).rbegin();
377 
378  RegionList silences;
379 
380  if (!FindSilences(silences, mOutputTracks.get(), track, last))
381  return false;
382  // Treat tracks in the sync lock group only
383  Track *groupFirst, *groupLast;
384  if (syncLock) {
385  auto trackRange = TrackList::SyncLockGroup(track);
386  groupFirst = *trackRange.begin();
387  groupLast = *trackRange.rbegin();
388  }
389  else {
390  groupFirst = track;
391  groupLast = last;
392  }
393  double totalCutLen = 0.0;
394  if (!DoRemoval(silences, iGroup, nGroups, groupFirst, groupLast, totalCutLen))
395  return false;
396  newT1 = std::max(newT1, mT1 - totalCutLen);
397 
398  ++iGroup;
399  }
400  }
401 
402  mT1 = newT1;
403 
404  return true;
405 }
406 
408 {
409  // Copy tracks
410  CopyInputTracks(true);
411 
412  // Master list of silent regions.
413  // This list should always be kept in order.
414  RegionList silences;
415 
416  auto trackRange0 = inputTracks()->Selected< const WaveTrack >();
417  if (FindSilences(
418  silences, inputTracks(), *trackRange0.begin(), *trackRange0.rbegin())) {
419  auto trackRange = mOutputTracks->Any();
420  double totalCutLen = 0.0;
421  if (DoRemoval(silences, 0, 1,
422  *trackRange.begin(), *trackRange.rbegin(), totalCutLen)) {
423  mT1 -= totalCutLen;
424  return true;
425  }
426  }
427 
428  return false;
429 }
430 
432  (RegionList &silences, const TrackList *list,
433  const Track *firstTrack, const Track *lastTrack)
434 {
435  // Start with the whole selection silent
436  silences.push_back(Region(mT0, mT1));
437 
438  // Remove non-silent regions in each track
439  int whichTrack = 0;
440  for (auto wt :
441  list->Selected< const WaveTrack >()
442  .StartingWith( firstTrack ).EndingAfter( lastTrack ) )
443  {
444  // Smallest silent region to detect in frames
445  auto minSilenceFrames =
446  sampleCount(std::max(mInitialAllowedSilence, DEF_MinTruncMs) * wt->GetRate());
447 
448  //
449  // Scan the track for silences
450  //
451  RegionList trackSilences;
452 
453  auto index = wt->TimeToLongSamples(mT0);
454  sampleCount silentFrame = 0;
455 
456  // Detect silences
457  bool cancelled = !(Analyze(silences, trackSilences, wt, &silentFrame, &index, whichTrack));
458 
459  // Buffer has been freed, so we're OK to return if cancelled
460  if (cancelled)
461  {
462  ReplaceProcessedTracks(false);
463  return false;
464  }
465 
466  if (silentFrame >= minSilenceFrames)
467  {
468  // Track ended in silence -- record region
469  trackSilences.push_back(Region(
470  wt->LongSamplesToTime(index - silentFrame),
471  wt->LongSamplesToTime(index)
472  ));
473  }
474 
475  // Intersect with the overall silent region list
476  Intersect(silences, trackSilences);
477  whichTrack++;
478  }
479 
480  return true;
481 }
482 
484 (const RegionList &silences, unsigned iGroup, unsigned nGroups, Track *firstTrack, Track *lastTrack,
485  double &totalCutLen)
486 {
487  //
488  // Now remove the silent regions from all selected / sync-lock selected tracks.
489  //
490 
491  // Loop over detected regions in reverse (so cuts don't change time values
492  // down the line)
493  int whichReg = 0;
494  RegionList::const_reverse_iterator rit;
495  for (rit = silences.rbegin(); rit != silences.rend(); ++rit)
496  {
497  const Region &region = *rit;
498  const Region *const r = &region;
499 
500  // Progress dialog and cancellation. Do additional cleanup before return.
501  const double frac = detectFrac +
502  (1 - detectFrac) * (iGroup + whichReg / double(silences.size())) / nGroups;
503  if (TotalProgress(frac))
504  {
505  ReplaceProcessedTracks(false);
506  return false;
507  }
508 
509  // Intersection may create regions smaller than allowed; ignore them.
510  // Allow one nanosecond extra for consistent results with exact milliseconds of allowed silence.
511  if ((r->end - r->start) < (mInitialAllowedSilence - 0.000000001))
512  continue;
513 
514  // Find NEW silence length as requested
515  double inLength = r->end - r->start;
516  double outLength;
517 
518  switch (mActionIndex)
519  {
520  case kTruncate:
521  outLength = std::min(mTruncLongestAllowedSilence, inLength);
522  break;
523  case kCompress:
524  outLength = mInitialAllowedSilence +
525  (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0;
526  break;
527  default: // Not currently used.
528  outLength = std::min(mInitialAllowedSilence +
529  (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0,
531  }
532 
533  const double cutLen = std::max(0.0, inLength - outLength);
534  // Don't waste time cutting nothing.
535  if( cutLen == 0.0 )
536  continue;
537 
538  totalCutLen += cutLen;
539 
540  double cutStart = (r->start + r->end - cutLen) / 2;
541  double cutEnd = cutStart + cutLen;
542  (mOutputTracks->Any()
543  .StartingWith(firstTrack).EndingAfter(lastTrack)
545  - [&](const Track *pTrack) { return
546  // Don't waste time past the end of a track
547  pTrack->GetEndTime() < r->start;
548  }
549  ).Visit(
550  [&](WaveTrack *wt) {
551 
552  // In WaveTracks, clear with a cross-fade
553  auto blendFrames = mBlendFrameCount;
554  // Round start/end times to frame boundaries
555  cutStart = wt->LongSamplesToTime(wt->TimeToLongSamples(cutStart));
556  cutEnd = wt->LongSamplesToTime(wt->TimeToLongSamples(cutEnd));
557 
558  // Make sure the cross-fade does not affect non-silent frames
559  if (wt->LongSamplesToTime(blendFrames) > inLength)
560  {
561  // Result is not more than blendFrames:
562  blendFrames = wt->TimeToLongSamples(inLength).as_size_t();
563  }
564 
565  // Perform cross-fade in memory
566  Floats buf1{ blendFrames };
567  Floats buf2{ blendFrames };
568  auto t1 = wt->TimeToLongSamples(cutStart) - blendFrames / 2;
569  auto t2 = wt->TimeToLongSamples(cutEnd) - blendFrames / 2;
570 
571  wt->GetFloats(buf1.get(), t1, blendFrames);
572  wt->GetFloats(buf2.get(), t2, blendFrames);
573 
574  for (decltype(blendFrames) i = 0; i < blendFrames; ++i)
575  {
576  buf1[i] = ((blendFrames-i) * buf1[i] + i * buf2[i]) /
577  (double)blendFrames;
578  }
579 
580  // Perform the cut
581  wt->Clear(cutStart, cutEnd);
582 
583  // Write cross-faded data
584  wt->Set((samplePtr)buf1.get(), floatSample, t1, blendFrames);
585  },
586  [&](Track *t) {
587  // Non-wave tracks: just do a sync-lock adjust
588  t->SyncLockAdjust(cutEnd, cutStart);
589  }
590  );
591  ++whichReg;
592  }
593 
594  return true;
595 }
596 
598  RegionList& trackSilences,
599  const WaveTrack *wt,
600  sampleCount* silentFrame,
601  sampleCount* index,
602  int whichTrack,
603  double* inputLength /*= NULL*/,
604  double* minInputLength /*= NULL*/)
605 {
606  // Smallest silent region to detect in frames
607  auto minSilenceFrames = sampleCount(std::max( mInitialAllowedSilence, DEF_MinTruncMs) * wt->GetRate());
608 
609  double truncDbSilenceThreshold = DB_TO_LINEAR( mThresholdDB );
610  auto blockLen = wt->GetMaxBlockSize();
611  auto start = wt->TimeToLongSamples(mT0);
612  auto end = wt->TimeToLongSamples(mT1);
613  sampleCount outLength = 0;
614 
615  double previewLength;
616  gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLength, 6.0);
617  // Minimum required length in samples.
618  const sampleCount previewLen( previewLength * wt->GetRate() );
619 
620  // Keep position in overall silences list for optimization
621  RegionList::iterator rit(silenceList.begin());
622 
623  // Allocate buffer
624  Floats buffer{ blockLen };
625 
626  // Loop through current track
627  while (*index < end) {
628  if (inputLength && ((outLength >= previewLen) || (*index - start > wt->TimeToLongSamples(*minInputLength)))) {
629  *inputLength = std::min<double>(*inputLength, *minInputLength);
630  if (outLength >= previewLen) {
631  *minInputLength = *inputLength;
632  }
633  return true;
634  }
635 
636  if (!inputLength) {
637  // Show progress dialog, test for cancellation
638  bool cancelled = TotalProgress(
639  detectFrac * (whichTrack +
640  (*index - start).as_double() /
641  (end - start).as_double()) /
642  (double)GetNumWaveTracks());
643  if (cancelled)
644  return false;
645  }
646 
647  // Optimization: if not in a silent region skip ahead to the next one
648 
649  double curTime = wt->LongSamplesToTime(*index);
650  for ( ; rit != silenceList.end(); ++rit) {
651  // Find the first silent region ending after current time
652  if (rit->end >= curTime) {
653  break;
654  }
655  }
656 
657  if (rit == silenceList.end()) {
658  // No more regions -- no need to process the rest of the track
659  if (inputLength) {
660  // Add available samples up to previewLength.
661  auto remainingTrackSamples = wt->TimeToLongSamples(wt->GetEndTime()) - *index;
662  auto requiredTrackSamples = previewLen - outLength;
663  outLength += (remainingTrackSamples > requiredTrackSamples)? requiredTrackSamples : remainingTrackSamples;
664  }
665 
666  break;
667  }
668  else if (rit->start > curTime) {
669  // End current silent region, skip ahead
670  if (*silentFrame >= minSilenceFrames) {
671  trackSilences.push_back(Region(
672  wt->LongSamplesToTime(*index - *silentFrame),
673  wt->LongSamplesToTime(*index)
674  ));
675  }
676  *silentFrame = 0;
677  auto newIndex = wt->TimeToLongSamples(rit->start);
678  if (inputLength) {
679  auto requiredTrackSamples = previewLen - outLength;
680  // Add non-silent sample to outLength
681  outLength += ((newIndex - *index) > requiredTrackSamples)? requiredTrackSamples : newIndex - *index;
682  }
683 
684  *index = newIndex;
685  }
686  // End of optimization
687 
688  // Limit size of current block if we've reached the end
689  auto count = limitSampleBufferSize( blockLen, end - *index );
690 
691  // Fill buffer
692  wt->GetFloats((buffer.get()), *index, count);
693 
694  // Look for silenceList in current block
695  for (decltype(count) i = 0; i < count; ++i) {
696  if (inputLength && ((outLength >= previewLen) || (outLength > wt->TimeToLongSamples(*minInputLength)))) {
697  *inputLength = wt->LongSamplesToTime(*index + i) - wt->LongSamplesToTime(start);
698  break;
699  }
700 
701  if (fabs(buffer[i]) < truncDbSilenceThreshold) {
702  (*silentFrame)++;
703  }
704  else {
705  sampleCount allowed = 0;
706  if (*silentFrame >= minSilenceFrames) {
707  if (inputLength) {
708  switch (mActionIndex) {
709  case kTruncate:
711  break;
712  case kCompress:
714  outLength += sampleCount(
715  allowed.as_double() +
716  (*silentFrame - allowed).as_double()
717  * mSilenceCompressPercent / 100.0
718  );
719  break;
720  // default: // Not currently used.
721  }
722  }
723 
724  // Record the silent region
725  trackSilences.push_back(Region(
726  wt->LongSamplesToTime(*index + i - *silentFrame),
727  wt->LongSamplesToTime(*index + i)
728  ));
729  }
730  else if (inputLength) { // included as part of non-silence
731  outLength += *silentFrame;
732  }
733  *silentFrame = 0;
734  if (inputLength) {
735  ++outLength; // Add non-silent sample to outLength
736  }
737  }
738  }
739  // Next block
740  *index += count;
741  }
742 
743  if (inputLength) {
744  *inputLength = std::min<double>(*inputLength, *minInputLength);
745  if (outLength >= previewLen) {
746  *minInputLength = *inputLength;
747  }
748  }
749 
750  return true;
751 }
752 
753 
755 {
756  wxASSERT(nActions == WXSIZEOF(kActionStrings));
757 
758  S.AddSpace(0, 5);
759 
760  S.StartStatic(XO("Detect Silence"));
761  {
762  S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
763  {
764  // Threshold
765  mThresholdText = S
766  .Validator<FloatingPointValidator<double>>(
767  3, &mThresholdDB, NumValidatorStyle::NO_TRAILING_ZEROES,
768  MIN_Threshold, MAX_Threshold
769  )
770  .NameSuffix(XO("db"))
771  .AddTextBox(XXO("&Threshold:"), wxT(""), 0);
772  S.AddUnits(XO("dB"));
773 
774  // Ignored silence
775  mInitialAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
777  NumValidatorStyle::NO_TRAILING_ZEROES,
778  MIN_Minimum, MAX_Minimum)
779  .NameSuffix(XO("seconds"))
780  .AddTextBox(XXO("&Duration:"), wxT(""), 12);
781  S.AddUnits(XO("seconds"));
782  }
783  S.EndMultiColumn();
784  }
785  S.EndStatic();
786 
787  S.StartStatic(XO("Action"));
788  {
789  S.StartHorizontalLay();
790  {
791  // Action choices
792  auto actionChoices = Msgids( kActionStrings, nActions );
793  mActionChoice = S
794  .Validator<wxGenericValidator>(&mActionIndex)
795  .MinSize( { -1, -1 } )
796  .AddChoice( {}, actionChoices );
797  }
798  S.EndHorizontalLay();
799  S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
800  {
801  // Truncation / Compression factor
802 
803  mTruncLongestAllowedSilenceT = S.Validator<FloatingPointValidator<double>>(
805  NumValidatorStyle::NO_TRAILING_ZEROES,
806  MIN_Truncate, MAX_Truncate
807  )
808  .NameSuffix(XO("seconds"))
809  .AddTextBox(XXO("Tr&uncate to:"), wxT(""), 12);
810  S.AddUnits(XO("seconds"));
811 
812  mSilenceCompressPercentT = S.Validator<FloatingPointValidator<double>>(
814  NumValidatorStyle::NO_TRAILING_ZEROES,
815  MIN_Compress, MAX_Compress
816  )
817  .NameSuffix(XO("%"))
818  .AddTextBox(XXO("C&ompress to:"), wxT(""), 12);
819  S.AddUnits(XO("%"));
820  }
821  S.EndMultiColumn();
822 
823  S.StartMultiColumn(2, wxALIGN_CENTER_HORIZONTAL);
824  {
825  mIndependent = S.AddCheckBox(XXO("Trunc&ate tracks independently"),
826  mbIndependent);
827  }
828  S.EndMultiColumn();
829 }
830  S.EndStatic();
831 
832  UpdateUI();
833 }
834 
836 {
837  if (!mUIParent->TransferDataToWindow())
838  {
839  return false;
840  }
841 
842  return true;
843 }
844 
846 {
847  if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
848  {
849  return false;
850  }
851 
852  mbIndependent = mIndependent->IsChecked();
853 
854  return true;
855 }
856 
857 // EffectTruncSilence implementation
858 
859 // Finds the intersection of the ordered region lists, stores in dest
861 {
862  RegionList::iterator destIter;
863  destIter = dest.begin();
864  // Any time we reach the end of the dest list we're finished
865  if (destIter == dest.end())
866  return;
867  RegionList::iterator curDest = destIter;
868 
869  // Operation: find non-silent regions in src, remove them from dest.
870  double nsStart = curDest->start;
871  double nsEnd;
872  bool lastRun = false; // must run the loop one extra time
873 
874  RegionList::const_iterator srcIter = src.begin();
875 
876  // This logic, causing the loop to run once after end of src, must occur
877  // each time srcIter is updated
878  if (srcIter == src.end())
879  {
880  lastRun = true;
881  }
882 
883  while (srcIter != src.end() || lastRun)
884  {
885  // Don't use curSrc unless lastRun is false!
886  RegionList::const_iterator curSrc;
887 
888  if (lastRun)
889  {
890  // The last non-silent region extends as far as possible
891  nsEnd = std::numeric_limits<double>::max();
892  }
893  else
894  {
895  curSrc = srcIter;
896  nsEnd = curSrc->start;
897  }
898 
899  if (nsEnd > nsStart)
900  {
901  // Increment through dest until we have a region that could be affected
902  while (curDest->end <= nsStart)
903  {
904  ++destIter;
905  if (destIter == dest.end())
906  {
907  return;
908  }
909  curDest = destIter;
910  }
911 
912  // Check for splitting dest region in two
913  if (nsStart > curDest->start && nsEnd < curDest->end)
914  {
915  // The second region
916  Region r(nsEnd, curDest->end);
917 
918  // The first region
919  curDest->end = nsStart;
920 
921  // Insert second region after first
922  RegionList::iterator nextIt(destIter);
923  ++nextIt;
924 
925  // This should just read: destIter = dest.insert(nextIt, r); but we
926  // work around two two wxList::insert() bugs. First, in some
927  // versions it returns the wrong value. Second, in some versions,
928  // it crashes when you insert at list end.
929  if (nextIt == dest.end())
930  dest.push_back(r);
931  else
932  dest.insert(nextIt, r);
933  ++destIter; // (now points at the newly-inserted region)
934 
935  curDest = destIter;
936  }
937 
938  // Check for truncating the end of dest region
939  if (nsStart > curDest->start && nsStart < curDest->end &&
940  nsEnd >= curDest->end)
941  {
942  curDest->end = nsStart;
943 
944  ++destIter;
945  if (destIter == dest.end())
946  {
947  return;
948  }
949  curDest = destIter;
950  }
951 
952  // Check for all dest regions that need to be removed completely
953  while (nsStart <= curDest->start && nsEnd >= curDest->end)
954  {
955  destIter = dest.erase(destIter);
956  if (destIter == dest.end())
957  {
958  return;
959  }
960  curDest = destIter;
961  }
962 
963  // Check for truncating the beginning of dest region
964  if (nsStart <= curDest->start &&
965  nsEnd > curDest->start && nsEnd < curDest->end)
966  {
967  curDest->start = nsEnd;
968  }
969  }
970 
971  if (lastRun)
972  {
973  // done
974  lastRun = false;
975  }
976  else
977  {
978  // Next non-silent region starts at the end of this silent region
979  nsStart = curSrc->end;
980  ++srcIter;
981  if (srcIter == src.end())
982  {
983  lastRun = true;
984  }
985  }
986  }
987 }
988 
989 /*
990 void EffectTruncSilence::BlendFrames(float* buffer, int blendFrameCount, int leftIndex, int rightIndex)
991 {
992  float* bufOutput = &buffer[leftIndex];
993  float* bufBefore = &buffer[leftIndex];
994  float* bufAfter = &buffer[rightIndex];
995  double beforeFactor = 1.0;
996  double afterFactor = 0.0;
997  double adjFactor = 1.0 / (double)blendFrameCount;
998  for (int j = 0; j < blendFrameCount; ++j)
999  {
1000  bufOutput[j] = (float)((bufBefore[j] * beforeFactor) + (bufAfter[j] * afterFactor));
1001  beforeFactor -= adjFactor;
1002  afterFactor += adjFactor;
1003  }
1004 }
1005 */
1006 
1008 {
1009  switch (mActionIndex)
1010  {
1011  case kTruncate:
1012  mTruncLongestAllowedSilenceT->Enable(true);
1013  mSilenceCompressPercentT->Enable(false);
1014  break;
1015  case kCompress:
1016  mTruncLongestAllowedSilenceT->Enable(false);
1017  mSilenceCompressPercentT->Enable(true);
1018  }
1019 }
1020 
1021 void EffectTruncSilence::OnControlChange(wxCommandEvent & WXUNUSED(evt))
1022 {
1023  mActionChoice->GetValidator()->TransferFromWindow();
1024 
1025  UpdateUI();
1026 
1027  if (!EnableApply(mUIParent->TransferDataFromWindow()))
1028  {
1029  return;
1030  }
1031 }
DEF_MinTruncMs
static const double DEF_MinTruncMs
Definition: TruncSilence.cpp:118
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
EffectTruncSilence::mTruncLongestAllowedSilence
double mTruncLongestAllowedSilence
Definition: TruncSilence.h:99
CommandParameters
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the Shuttle cla...
Definition: EffectAutomationParameters.h:67
TruncSilence.h
Effect::FindProject
const AudacityProject * FindProject() const
Definition: Effect.cpp:2271
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:69
EffectTypeProcess
@ EffectTypeProcess
Definition: EffectInterface.h:59
DB_TO_LINEAR
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
ShuttleGuiBase::AddCheckBox
wxCheckBox * AddCheckBox(const TranslatableString &Prompt, bool Selected)
Definition: ShuttleGui.cpp:309
EffectTruncSilence::mThresholdText
wxTextCtrl * mThresholdText
Definition: TruncSilence.h:105
Effect::EnableApply
virtual bool EnableApply(bool enable=true)
Definition: Effect.cpp:1925
Enums::DbChoices
static const EnumValueSymbol DbChoices[]
Definition: TruncSilence.cpp:43
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
kObsoleteActions
static CommandParameters::ObsoleteMap kObsoleteActions[]
Definition: TruncSilence.cpp:86
Effect::MessageBox
int MessageBox(const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={})
Definition: Effect.cpp:2481
WaveTrack::GetEndTime
double GetEndTime() const override
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1800
EffectTruncSilence::mTruncLongestAllowedSilenceT
wxTextCtrl * mTruncLongestAllowedSilenceT
Definition: TruncSilence.h:108
Effect::CopyInputTracks
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:2070
EffectTruncSilence::TransferDataFromWindow
bool TransferDataFromWindow() override
Definition: TruncSilence.cpp:845
EffectTruncSilence::DefineParams
bool DefineParams(ShuttleParams &S) override
Definition: TruncSilence.cpp:185
TrackList::Channels
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1468
nObsoleteActions
static const size_t nObsoleteActions
Definition: TruncSilence.cpp:92
TrackList
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1264
EffectTruncSilence::mInitialAllowedSilenceT
wxTextCtrl * mInitialAllowedSilenceT
Definition: TruncSilence.h:107
Effect::mT1
double mT1
Definition: Effect.h:467
kTruncate
@ kTruncate
Definition: TruncSilence.cpp:75
ShuttleGui::AddSpace
wxSizerItem * AddSpace(int width, int height, int prop=0)
Definition: ShuttleGui.cpp:2447
EffectTruncSilence::mSilenceCompressPercent
double mSilenceCompressPercent
Definition: TruncSilence.h:100
EffectTruncSilence::mbIndependent
bool mbIndependent
Definition: TruncSilence.h:101
XO
#define XO(s)
Definition: Internat.h:31
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:40
EffectTruncSilence::TransferDataToWindow
bool TransferDataToWindow() override
Definition: TruncSilence.cpp:835
EffectTruncSilence::~EffectTruncSilence
virtual ~EffectTruncSilence()
Definition: TruncSilence.cpp:155
Region::start
double start
Definition: WaveTrack.h:56
ShuttleParams
Shuttle that deals with parameters. This is a base class with lots of virtual functions that do nothi...
Definition: Shuttle.h:62
ShuttleGuiBase::EndMultiColumn
void EndMultiColumn()
Definition: ShuttleGui.cpp:1238
RegionList
Definition: TruncSilence.cpp:71
anonymous_namespace{TruncSilence.cpp}::reg
BuiltinEffectsModule::Registration< EffectTruncSilence > reg
Definition: TruncSilence.cpp:126
Effect::SaveUserPreset
bool SaveUserPreset(const RegistryPath &name) override
Definition: Effect.cpp:569
enumToDB
static double enumToDB(int val)
Definition: TruncSilence.cpp:66
ComponentInterfaceSymbol
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Definition: ComponentInterfaceSymbol.h:27
EffectTruncSilence::Analyze
bool Analyze(RegionList &silenceList, RegionList &trackSilences, const WaveTrack *wt, sampleCount *silentFrame, sampleCount *index, int whichTrack, double *inputLength=NULL, double *minInputLength=NULL)
Definition: TruncSilence.cpp:597
WaveTrack::Set
void Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len)
Definition: WaveTrack.cpp:1981
floatSample
@ floatSample
Definition: SampleFormat.h:34
EffectTruncSilence::ManualPage
ManualPageID ManualPage() override
Definition: TruncSilence.cpp:171
EffectTruncSilence::ProcessIndependently
bool ProcessIndependently()
Definition: TruncSilence.cpp:334
kActions
kActions
Definition: TruncSilence.cpp:74
ReadAndVerifyEnum
#define ReadAndVerifyEnum(name, list, listSize)
Definition: Effect.h:621
Registry::Visit
void Visit(Visitor &visitor, BaseItem *pTopItem, const GroupItem *pRegistry)
Definition: Registry.cpp:713
ReadAndVerifyEnumWithObsoletes
#define ReadAndVerifyEnumWithObsoletes(name, list, listSize, obsoleteList, nObsolete)
Definition: Effect.h:626
XXO
#define XXO(s)
Definition: Internat.h:44
WaveTrack::Clear
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:652
Effect::inputTracks
const TrackList * inputTracks() const
Definition: Effect.h:463
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1177
kCompress
@ kCompress
Definition: TruncSilence.cpp:76
Effect::mT0
double mT0
Definition: Effect.h:466
sampleCount::as_double
double as_double() const
Definition: SampleCount.h:45
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1167
WaveTrack::LongSamplesToTime
double LongSamplesToTime(sampleCount pos) const
Convert correctly between a number of samples and an (absolute) time in seconds.
Definition: WaveTrack.cpp:1775
ShuttleGuiBase::StartMultiColumn
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:1229
TrackList::begin
iterator begin()
Definition: Track.h:1308
Enums
Enums is a helper class for Shuttle. It defines enumerations which are used in effects dialogs,...
Definition: TruncSilence.cpp:40
EffectTruncSilence::ProcessAll
bool ProcessAll()
Definition: TruncSilence.cpp:407
ShuttleGuiBase::AddUnits
void AddUnits(const TranslatableString &Prompt, int wrapWidth=0)
Left aligned text string.
Definition: ShuttleGui.cpp:263
CommandParameters::ObsoleteMap
std::pair< wxString, size_t > ObsoleteMap
Definition: EffectAutomationParameters.h:163
Effect::ReplaceProcessedTracks
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: Effect.cpp:2192
Track::IsSelected
bool IsSelected() const
Definition: Track.cpp:364
nActions
@ nActions
Definition: TruncSilence.cpp:77
ShuttleGui::Validator
ShuttleGui & Validator(const Factory &f)
Definition: ShuttleGui.h:678
EffectTruncSilence::mActionIndex
int mActionIndex
Definition: TruncSilence.h:97
Region::end
double end
Definition: WaveTrack.h:56
Effect::mOutputTracks
std::shared_ptr< TrackList > mOutputTracks
Definition: Effect.h:465
Msgids
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Convenience function often useful when adding choice controls.
Definition: ShuttleGui.cpp:2510
sampleCount::as_size_t
size_t as_size_t() const
Definition: SampleCount.cpp:15
ReadAndVerifyDouble
#define ReadAndVerifyDouble(name)
Definition: Effect.h:633
Effect::GetCurrentSettingsGroup
RegistryPath GetCurrentSettingsGroup() override
Definition: Effect.cpp:865
LoadEffects.h
EffectTruncSilence::Process
bool Process() override
Definition: TruncSilence.cpp:321
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
ShuttleGuiBase::StartStatic
wxStaticBox * StartStatic(const TranslatableString &Str, int iProp=0)
Definition: ShuttleGui.cpp:893
WaveTrack::GetMaxBlockSize
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:1581
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
EffectTruncSilence::Intersect
void Intersect(RegionList &dest, const RegionList &src)
Definition: TruncSilence.cpp:860
EffectTruncSilence::CalcPreviewInputLength
double CalcPreviewInputLength(double previewLength) override
Definition: TruncSilence.cpp:243
EffectTruncSilence::mActionChoice
wxChoice * mActionChoice
Definition: TruncSilence.h:106
EffectTruncSilence
Truncate Silence automatically reduces the length of passages where the volume is below a set thresho...
Definition: TruncSilence.h:30
EffectTruncSilence::mInitialAllowedSilence
double mInitialAllowedSilence
Definition: TruncSilence.h:98
WaveTrack::TimeToLongSamples
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: WaveTrack.cpp:1770
BuiltinEffectsModule::Registration
Definition: LoadEffects.h:40
DEF_BlendFrameCount
static const size_t DEF_BlendFrameCount
Definition: TruncSilence.cpp:114
Track
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:239
Effect::mUIParent
wxWindow * mUIParent
Definition: Effect.h:478
TaggedIdentifier< ManualPageIDTag >
EffectTruncSilence::UpdateUI
void UpdateUI()
Definition: TruncSilence.cpp:1007
EffectTruncSilence::GetSymbol
ComponentInterfaceSymbol GetSymbol() override
Definition: TruncSilence.cpp:161
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
EffectTruncSilence::SetAutomationParameters
bool SetAutomationParameters(CommandParameters &parms) override
Definition: TruncSilence.cpp:207
EffectTruncSilence::PopulateOrExchange
void PopulateOrExchange(ShuttleGui &S) override
Definition: TruncSilence.cpp:754
EffectTruncSilence::FindSilences
bool FindSilences(RegionList &silences, const TrackList *list, const Track *firstTrack, const Track *lastTrack)
Definition: TruncSilence.cpp:432
EffectTruncSilence::Symbol
static const ComponentInterfaceSymbol Symbol
Definition: TruncSilence.h:32
ExceptionType::Internal
@ Internal
Indicates internal failure from Audacity.
EffectTruncSilence::GetDescription
TranslatableString GetDescription() override
Definition: TruncSilence.cpp:166
Effect::TotalProgress
bool TotalProgress(double frac, const TranslatableString &={})
Definition: Effect.cpp:2016
EffectTruncSilence::Startup
bool Startup() override
Definition: TruncSilence.cpp:270
Effect::GetNumWaveTracks
int GetNumWaveTracks()
Definition: Effect.h:345
EffectTruncSilence::DoRemoval
bool DoRemoval(const RegionList &silences, unsigned iGroup, unsigned nGroups, Track *firstTrack, Track *lastTrack, double &totalCutLen)
Definition: TruncSilence.cpp:484
Region
#define Region
Definition: VSTControlGTK.h:16
Track::IsSelectedOrSyncLockSelected
bool IsSelectedOrSyncLockSelected() const
Definition: Track.cpp:367
detectFrac
const double detectFrac
Definition: TruncSilence.cpp:121
Prefs.h
Param
Param(DbIndex, int, wxT("Db"), 0, 0, Enums::NumDbChoices - 1, 1)
EffectTruncSilence::mThresholdDB
double mThresholdDB
Definition: TruncSilence.h:96
ShuttleGuiBase::EndStatic
void EndStatic()
Definition: ShuttleGui.cpp:922
EffectType
EffectType
Definition: EffectInterface.h:55
TrackList::SyncLockGroup
static TrackIterRange< Track > SyncLockGroup(Track *pTrack)
Definition: Track.cpp:639
Region
Structure to hold region of a wavetrack and a comparison function for sortability.
Definition: WaveTrack.h:52
settings
static Settings & settings()
Definition: TrackInfo.cpp:86
EffectTruncSilence::mIndependent
wxCheckBox * mIndependent
Definition: TruncSilence.h:110
EffectTruncSilence::GetType
EffectType GetType() override
Definition: TruncSilence.cpp:178
limitSampleBufferSize
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:21
WaveTrack::GetFloats
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
Retrieve samples from a track in floating-point format, regardless of the storage format.
Definition: WaveTrack.h:261
EffectTruncSilence::GetAutomationParameters
bool GetAutomationParameters(CommandParameters &parms) override
Definition: TruncSilence.cpp:195
TrackList::Selected
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1372
END_EVENT_TABLE
END_EVENT_TABLE()
ArrayOf< float >
EffectTruncSilence::OnControlChange
void OnControlChange(wxCommandEvent &evt)
Definition: TruncSilence.cpp:1021
kActionStrings
static const EnumValueSymbol kActionStrings[nActions]
Definition: TruncSilence.cpp:80
EffectTruncSilence::mSilenceCompressPercentT
wxTextCtrl * mSilenceCompressPercentT
Definition: TruncSilence.h:109
ReadAndVerifyBool
#define ReadAndVerifyBool(name)
Definition: Effect.h:635
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:631
EffectTruncSilence::mBlendFrameCount
size_t mBlendFrameCount
Definition: TruncSilence.h:103
Enums::NumDbChoices
static const size_t NumDbChoices
Definition: TruncSilence.cpp:42
WaveTrack::GetRate
double GetRate() const
Definition: WaveTrack.cpp:358