Audacity  2.2.2
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 #include "../Audacity.h"
19 #include "TruncSilence.h"
20 
21 #include <algorithm>
22 #include <list>
23 #include <limits>
24 #include <math.h>
25 
26 #include <wx/checkbox.h>
27 #include <wx/choice.h>
28 #include <wx/valgen.h>
29 
30 #include "../Prefs.h"
31 #include "../Project.h"
32 #include "../ShuttleGui.h"
33 #include "../WaveTrack.h"
34 #include "../widgets/valnum.h"
35 #include "../widgets/ErrorDialog.h"
36 
37 // Declaration of RegionList
38 class RegionList : public std::list < Region > {};
39 
41 {
45 };
46 
47 static const wxChar *kActionStrings[kNumActions] =
48 {
49  XO("Truncate Detected Silence"),
50  XO("Compress Excess Silence")
51 };
52 
53 // Define defaults, minimums, and maximums for each parameter
54 #define DefaultAndLimits(name, def, min, max) \
55  static const double DEF_ ## name = (def); \
56  static const double MIN_ ## name = (min); \
57  static const double MAX_ ## name = (max);
58 
59 // Define keys, defaults, minimums, and maximums for the effect parameters
60 //
61 // Name Type Key Def Min Max Scale
62 Param( DbIndex, int, wxT("Db"), 0, 0, Enums::NumDbChoices - 1, 1 );
63 Param( ActIndex, int, wxT("Action"), kTruncate, 0, kNumActions - 1, 1 );
64 Param( Minimum, double, wxT("Minimum"), 0.5, 0.001, 10000.0, 1 );
65 Param( Truncate, double, wxT("Truncate"), 0.5, 0.0, 10000.0, 1 );
66 Param( Compress, double, wxT("Compress"), 50.0, 0.0, 99.9, 1 );
67 Param( Independent, bool, wxT("Independent"), false, false, true, 1 );
68 
69 static const size_t DEF_BlendFrameCount = 100;
70 
71 // Lower bound on the amount of silence to find at a time -- this avoids
72 // detecting silence repeatedly in low-frequency sounds.
73 static const double DEF_MinTruncMs = 0.001;
74 
75 // Typical fraction of total time taken by detection (better to guess low)
76 const double detectFrac = 0.4;
77 
78 BEGIN_EVENT_TABLE(EffectTruncSilence, wxEvtHandler)
79  EVT_CHOICE(wxID_ANY, EffectTruncSilence::OnControlChange)
80  EVT_TEXT(wxID_ANY, EffectTruncSilence::OnControlChange)
82 
84 {
85  mDbChoices = wxArrayString(Enums::NumDbChoices, Enums::GetDbChoices());
86 
87  mInitialAllowedSilence = DEF_Minimum;
88  mTruncLongestAllowedSilence = DEF_Truncate;
89  mSilenceCompressPercent = DEF_Compress;
90  mTruncDbChoiceIndex = DEF_DbIndex;
91  mActionIndex = DEF_ActIndex;
92  mbIndependent = DEF_Independent;
93 
94  SetLinearEffectFlag(false);
95 
96  // This used to be changeable via the audacity.cfg/registery. Doubtful that was
97  // ever done.
98  //
99  // Original comment:
100  //
101  // mBlendFrameCount only retrieved from prefs ... not using dialog
102  // Only way to change (for windows) is thru registry
103  // The values should be figured dynamically ... too many frames could be invalid
104  mBlendFrameCount = DEF_BlendFrameCount;
105 }
106 
108 {
109 }
110 
111 // IdentInterface implementation
112 
114 {
116 }
117 
119 {
120  return _("Automatically reduces the length of passages where the volume is below a specified level");
121 }
122 
124 {
125  return wxT("Truncate_Silence");
126 }
127 
128 // EffectIdentInterface implementation
129 
131 {
132  return EffectTypeProcess;
133 }
134 
135 // EffectClientInterface implementation
136 
137 bool EffectTruncSilence::GetAutomationParameters(EffectAutomationParameters & parms)
138 {
139  parms.Write(KEY_DbIndex, Enums::DbChoices[mTruncDbChoiceIndex]);
140  parms.Write(KEY_ActIndex, kActionStrings[mActionIndex]);
141  parms.Write(KEY_Minimum, mInitialAllowedSilence);
142  parms.Write(KEY_Truncate, mTruncLongestAllowedSilence);
143  parms.Write(KEY_Compress, mSilenceCompressPercent);
144  parms.Write(KEY_Independent, mbIndependent);
145 
146  return true;
147 }
148 
149 bool EffectTruncSilence::SetAutomationParameters(EffectAutomationParameters & parms)
150 {
151  wxArrayString actions(kNumActions, kActionStrings);
152  actions.Insert(wxT("0"), 0); // Compatible with 2.1.0 and before
153  actions.Insert(wxT("1"), 1); // Compatible with 2.1.0 and before
154 
155  ReadAndVerifyDouble(Minimum);
156  ReadAndVerifyDouble(Truncate);
157  ReadAndVerifyDouble(Compress);
158  ReadAndVerifyEnum(DbIndex, mDbChoices);
159  ReadAndVerifyEnum(ActIndex, actions);
160  ReadAndVerifyBool(Independent);
161 
162  mInitialAllowedSilence = Minimum;
163  mTruncLongestAllowedSilence = Truncate;
164  mSilenceCompressPercent = Compress;
165  mTruncDbChoiceIndex = DbIndex;
166  mActionIndex = ActIndex;
167  mbIndependent = Independent;
168 
169  // Readjust for 2.1.0 or before
170  if (mActionIndex >= kNumActions)
171  {
173  }
174 
175  return true;
176 }
177 
178 // Effect implementation
179 
180 double EffectTruncSilence::CalcPreviewInputLength(double /* previewLength */)
181 {
182  double inputLength = mT1 - mT0;
183  double minInputLength = inputLength;
184 
185  // Master list of silent regions
186  RegionList silences;
187 
188  // Start with the whole selection silent
189  silences.push_back(Region(mT0, mT1));
190 
192  int whichTrack = 0;
193 
194  for (Track *t = iter.First(); t; t = iter.Next()) {
195  WaveTrack *const wt = static_cast<WaveTrack *>(t);
196 
197  RegionList trackSilences;
198 
199  auto index = wt->TimeToLongSamples(mT0);
200  sampleCount silentFrame = 0; // length of the current silence
201 
202  Analyze(silences, trackSilences, wt, &silentFrame, &index, whichTrack, &inputLength, &minInputLength);
203 
204  whichTrack++;
205  }
206  return inputLength;
207 }
208 
209 
211 {
212  wxString base = wxT("/Effects/TruncateSilence/");
213 
214  // Migrate settings from 2.1.0 or before
215 
216  // Already migrated, so bail
217  if (gPrefs->Exists(base + wxT("Migrated")))
218  {
219  return true;
220  }
221 
222  // Load the old "current" settings
223  if (gPrefs->Exists(base))
224  {
225  mTruncDbChoiceIndex = gPrefs->Read(base + wxT("DbChoiceIndex"), 4L);
227  { // corrupted Prefs?
228  mTruncDbChoiceIndex = 4L;
229  }
230  mActionIndex = gPrefs->Read(base + wxT("ProcessChoice"), 0L);
231  if ((mActionIndex < 0) || (mActionIndex > 1))
232  { // corrupted Prefs?
233  mActionIndex = 0L;
234  }
235  gPrefs->Read(base + wxT("InitialAllowedSilence"), &mInitialAllowedSilence, 0.5);
236  if ((mInitialAllowedSilence < 0.001) || (mInitialAllowedSilence > 10000.0))
237  { // corrupted Prefs?
239  }
240  gPrefs->Read(base + wxT("LongestAllowedSilence"), &mTruncLongestAllowedSilence, 0.5);
241  if ((mTruncLongestAllowedSilence < 0.0) || (mTruncLongestAllowedSilence > 10000.0))
242  { // corrupted Prefs?
244  }
245  gPrefs->Read(base + wxT("CompressPercent"), &mSilenceCompressPercent, 50.0);
246  if ((mSilenceCompressPercent < 0.0) || (mSilenceCompressPercent > 100.0))
247  { // corrupted Prefs?
249  }
250 
252  }
253 
254  // Do not migrate again
255  gPrefs->Write(base + wxT("Migrated"), true);
256 
257  return true;
258 }
259 
261 {
262  const bool success =
265  : ProcessAll();
266 
267  if (success)
269 
270  return success;
271 }
272 
274 {
275  unsigned nGroups = 0;
276 
277  const bool syncLock = ::GetActiveProject()->IsSyncLocked();
278 
279  // Check if it's permissible
280  {
282  for (Track *track = iter.First(); track;
283  track = iter.Next(true) // skip linked tracks
284  ) {
285  if (syncLock) {
286  Track *const link = track->GetLink();
288  for (Track *track2 = syncIter.StartWith(track); track2; track2 = syncIter.Next()) {
289  if (track2->GetKind() == Track::Wave &&
290  !(track2 == track || track2 == link) &&
291  track2->GetSelected()) {
292  ::Effect::MessageBox(_("When truncating independently, there may only be one selected audio track in each Sync-Locked Track Group."));
293  return false;
294  }
295  }
296  }
297 
298  ++nGroups;
299  }
300  }
301 
302  if (nGroups == 0)
303  // nothing to do
304  return true;
305 
306  // Now do the work
307 
308  // Copy tracks
310  double newT1 = 0.0;
311 
312  {
313  unsigned iGroup = 0;
315  for (Track *track = iter.First(); track;
316  ++iGroup, track = iter.Next(true) // skip linked tracks
317  ) {
318  Track *const link = track->GetLink();
319  Track *const last = link ? link : track;
320 
321  RegionList silences;
322 
323  if (!FindSilences(silences, mOutputTracks.get(), track, last))
324  return false;
325  // Treat tracks in the sync lock group only
326  Track *groupFirst, *groupLast;
327  if (syncLock) {
328  SyncLockedTracksIterator syncIter(mOutputTracks.get());
329  groupFirst = syncIter.StartWith(track);
330  groupLast = syncIter.Last();
331  }
332  else {
333  groupFirst = track;
334  groupLast = last;
335  }
336  double totalCutLen = 0.0;
337  if (!DoRemoval(silences, iGroup, nGroups, groupFirst, groupLast, totalCutLen))
338  return false;
339  newT1 = std::max(newT1, mT1 - totalCutLen);
340  }
341  }
342 
343  mT1 = newT1;
344 
345  return true;
346 }
347 
349 {
350  // Copy tracks
352 
353  // Master list of silent regions.
354  // This list should always be kept in order.
355  RegionList silences;
356 
358  if (FindSilences(silences, inputTracks(), iter.First(), iter.Last())) {
359  TrackListIterator iterOut(mOutputTracks.get());
360  double totalCutLen = 0.0;
361  Track *const first = iterOut.First();
362  if (DoRemoval(silences, 0, 1, first, iterOut.Last(), totalCutLen)) {
363  mT1 -= totalCutLen;
364  return true;
365  }
366  }
367 
368  return false;
369 }
370 
372  (RegionList &silences, TrackList *list, Track *firstTrack, Track *lastTrack)
373 {
374  // Start with the whole selection silent
375  silences.push_back(Region(mT0, mT1));
376 
377  // Remove non-silent regions in each track
379  int whichTrack = 0;
380  bool lastSeen = false;
381  for (Track *t = iter.StartWith(firstTrack); !lastSeen && t; t = iter.Next())
382  {
383  lastSeen = (t == lastTrack);
384  WaveTrack *const wt = static_cast<WaveTrack *>(t);
385 
386  // Smallest silent region to detect in frames
387  auto minSilenceFrames =
388  sampleCount(std::max(mInitialAllowedSilence, DEF_MinTruncMs) * wt->GetRate());
389 
390  //
391  // Scan the track for silences
392  //
393  RegionList trackSilences;
394 
395  auto index = wt->TimeToLongSamples(mT0);
396  sampleCount silentFrame = 0;
397 
398  // Detect silences
399  bool cancelled = !(Analyze(silences, trackSilences, wt, &silentFrame, &index, whichTrack));
400 
401  // Buffer has been freed, so we're OK to return if cancelled
402  if (cancelled)
403  {
404  ReplaceProcessedTracks(false);
405  return false;
406  }
407 
408  if (silentFrame >= minSilenceFrames)
409  {
410  // Track ended in silence -- record region
411  trackSilences.push_back(Region(
412  wt->LongSamplesToTime(index - silentFrame),
413  wt->LongSamplesToTime(index)
414  ));
415  }
416 
417  // Intersect with the overall silent region list
418  Intersect(silences, trackSilences);
419  whichTrack++;
420  }
421 
422  return true;
423 }
424 
426 (const RegionList &silences, unsigned iGroup, unsigned nGroups, Track *firstTrack, Track *lastTrack,
427  double &totalCutLen)
428 {
429  //
430  // Now remove the silent regions from all selected / sync-lock selected tracks.
431  //
432 
433  // Loop over detected regions in reverse (so cuts don't change time values
434  // down the line)
435  int whichReg = 0;
436  RegionList::const_reverse_iterator rit;
437  for (rit = silences.rbegin(); rit != silences.rend(); ++rit)
438  {
439  const Region &region = *rit;
440  const Region *const r = &region;
441 
442  // Progress dialog and cancellation. Do additional cleanup before return.
443  const double frac = detectFrac +
444  (1 - detectFrac) * (iGroup + whichReg / double(silences.size())) / nGroups;
445  if (TotalProgress(frac))
446  {
447  ReplaceProcessedTracks(false);
448  return false;
449  }
450 
451  // Intersection may create regions smaller than allowed; ignore them.
452  // Allow one nanosecond extra for consistent results with exact milliseconds of allowed silence.
453  if ((r->end - r->start) < (mInitialAllowedSilence - 0.000000001))
454  continue;
455 
456  // Find NEW silence length as requested
457  double inLength = r->end - r->start;
458  double outLength;
459 
460  switch (mActionIndex)
461  {
462  case kTruncate:
463  outLength = std::min(mTruncLongestAllowedSilence, inLength);
464  break;
465  case kCompress:
466  outLength = mInitialAllowedSilence +
467  (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0;
468  break;
469  default: // Not currently used.
470  outLength = std::min(mInitialAllowedSilence +
471  (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0,
472  mTruncLongestAllowedSilence);
473  }
474 
475  double cutLen = std::max(0.0, inLength - outLength);
476  totalCutLen += cutLen;
477 
478  TrackListIterator iterOut(mOutputTracks.get());
479  bool lastSeen = false;
480  for (Track *t = iterOut.StartWith(firstTrack); t && !lastSeen; t = iterOut.Next())
481  {
482  lastSeen = (t == lastTrack);
483  if (!(t->GetSelected() || t->IsSyncLockSelected()))
484  continue;
485 
486  // Don't waste time past the end of a track
487  if (t->GetEndTime() < r->start)
488  continue;
489 
490  // Don't waste time cutting nothing.
491  if( cutLen == 0.0 )
492  continue;
493 
494  double cutStart = (r->start + r->end - cutLen) / 2;
495  double cutEnd = cutStart + cutLen;
496  if (t->GetKind() == Track::Wave)
497  {
498  // In WaveTracks, clear with a cross-fade
499  WaveTrack *const wt = static_cast<WaveTrack*>(t);
500  auto blendFrames = mBlendFrameCount;
501  // Round start/end times to frame boundaries
502  cutStart = wt->LongSamplesToTime(wt->TimeToLongSamples(cutStart));
503  cutEnd = wt->LongSamplesToTime(wt->TimeToLongSamples(cutEnd));
504 
505  // Make sure the cross-fade does not affect non-silent frames
506  if (wt->LongSamplesToTime(blendFrames) > inLength)
507  {
508  // Result is not more than blendFrames:
509  blendFrames = wt->TimeToLongSamples(inLength).as_size_t();
510  }
511 
512  // Perform cross-fade in memory
513  Floats buf1{ blendFrames };
514  Floats buf2{ blendFrames };
515  auto t1 = wt->TimeToLongSamples(cutStart) - blendFrames / 2;
516  auto t2 = wt->TimeToLongSamples(cutEnd) - blendFrames / 2;
517 
518  wt->Get((samplePtr)buf1.get(), floatSample, t1, blendFrames);
519  wt->Get((samplePtr)buf2.get(), floatSample, t2, blendFrames);
520 
521  for (decltype(blendFrames) i = 0; i < blendFrames; ++i)
522  {
523  buf1[i] = ((blendFrames-i) * buf1[i] + i * buf2[i]) /
524  (double)blendFrames;
525  }
526 
527  // Perform the cut
528  wt->Clear(cutStart, cutEnd);
529 
530  // Write cross-faded data
531  wt->Set((samplePtr)buf1.get(), floatSample, t1, blendFrames);
532  }
533  else
534  // Non-wave tracks: just do a sync-lock adjust
535  t->SyncLockAdjust(cutEnd, cutStart);
536  }
537  ++whichReg;
538  }
539 
540  return true;
541 }
542 
544  RegionList& trackSilences,
545  const WaveTrack *wt,
546  sampleCount* silentFrame,
547  sampleCount* index,
548  int whichTrack,
549  double* inputLength /*= NULL*/,
550  double* minInputLength /*= NULL*/)
551 {
552  // Smallest silent region to detect in frames
553  auto minSilenceFrames = sampleCount(std::max( mInitialAllowedSilence, DEF_MinTruncMs) * wt->GetRate());
554 
555  double truncDbSilenceThreshold = Enums::Db2Signal[mTruncDbChoiceIndex];
556  auto blockLen = wt->GetMaxBlockSize();
557  auto start = wt->TimeToLongSamples(mT0);
558  auto end = wt->TimeToLongSamples(mT1);
559  sampleCount outLength = 0;
560 
561  double previewLength;
562  gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLength, 6.0);
563  // Minimum required length in samples.
564  const sampleCount previewLen( previewLength * wt->GetRate() );
565 
566  // Keep position in overall silences list for optimization
567  RegionList::iterator rit(silenceList.begin());
568 
569  // Allocate buffer
570  Floats buffer{ blockLen };
571 
572  // Loop through current track
573  while (*index < end) {
574  if (inputLength && ((outLength >= previewLen) || (*index - start > wt->TimeToLongSamples(*minInputLength)))) {
575  *inputLength = std::min<double>(*inputLength, *minInputLength);
576  if (outLength >= previewLen) {
577  *minInputLength = *inputLength;
578  }
579  return true;
580  }
581 
582  if (!inputLength) {
583  // Show progress dialog, test for cancellation
584  bool cancelled = TotalProgress(
585  detectFrac * (whichTrack +
586  (*index - start).as_double() /
587  (end - start).as_double()) /
588  (double)GetNumWaveTracks());
589  if (cancelled)
590  return false;
591  }
592 
593  // Optimization: if not in a silent region skip ahead to the next one
594 
595  double curTime = wt->LongSamplesToTime(*index);
596  for ( ; rit != silenceList.end(); ++rit) {
597  // Find the first silent region ending after current time
598  if (rit->end >= curTime) {
599  break;
600  }
601  }
602 
603  if (rit == silenceList.end()) {
604  // No more regions -- no need to process the rest of the track
605  if (inputLength) {
606  // Add available samples up to previewLength.
607  auto remainingTrackSamples = wt->TimeToLongSamples(wt->GetEndTime()) - *index;
608  auto requiredTrackSamples = previewLen - outLength;
609  outLength += (remainingTrackSamples > requiredTrackSamples)? requiredTrackSamples : remainingTrackSamples;
610  }
611 
612  break;
613  }
614  else if (rit->start > curTime) {
615  // End current silent region, skip ahead
616  if (*silentFrame >= minSilenceFrames) {
617  trackSilences.push_back(Region(
618  wt->LongSamplesToTime(*index - *silentFrame),
619  wt->LongSamplesToTime(*index)
620  ));
621  }
622  *silentFrame = 0;
623  auto newIndex = wt->TimeToLongSamples(rit->start);
624  if (inputLength) {
625  auto requiredTrackSamples = previewLen - outLength;
626  // Add non-silent sample to outLength
627  outLength += ((newIndex - *index) > requiredTrackSamples)? requiredTrackSamples : newIndex - *index;
628  }
629 
630  *index = newIndex;
631  }
632  // End of optimization
633 
634  // Limit size of current block if we've reached the end
635  auto count = limitSampleBufferSize( blockLen, end - *index );
636 
637  // Fill buffer
638  wt->Get((samplePtr)(buffer.get()), floatSample, *index, count);
639 
640  // Look for silenceList in current block
641  for (decltype(count) i = 0; i < count; ++i) {
642  if (inputLength && ((outLength >= previewLen) || (outLength > wt->TimeToLongSamples(*minInputLength)))) {
643  *inputLength = wt->LongSamplesToTime(*index + i) - wt->LongSamplesToTime(start);
644  break;
645  }
646 
647  if (fabs(buffer[i]) < truncDbSilenceThreshold) {
648  (*silentFrame)++;
649  }
650  else {
651  sampleCount allowed = 0;
652  if (*silentFrame >= minSilenceFrames) {
653  if (inputLength) {
654  switch (mActionIndex) {
655  case kTruncate:
657  break;
658  case kCompress:
660  outLength += sampleCount(
661  allowed.as_double() +
662  (*silentFrame - allowed).as_double()
663  * mSilenceCompressPercent / 100.0
664  );
665  break;
666  // default: // Not currently used.
667  }
668  }
669 
670  // Record the silent region
671  trackSilences.push_back(Region(
672  wt->LongSamplesToTime(*index + i - *silentFrame),
673  wt->LongSamplesToTime(*index + i)
674  ));
675  }
676  else if (inputLength) { // included as part of non-silence
677  outLength += *silentFrame;
678  }
679  *silentFrame = 0;
680  if (inputLength) {
681  ++outLength; // Add non-silent sample to outLength
682  }
683  }
684  }
685  // Next block
686  *index += count;
687  }
688 
689  if (inputLength) {
690  *inputLength = std::min<double>(*inputLength, *minInputLength);
691  if (outLength >= previewLen) {
692  *minInputLength = *inputLength;
693  }
694  }
695 
696  return true;
697 }
698 
699 
701 {
702  wxASSERT(kNumActions == WXSIZEOF(kActionStrings));
703 
704  wxArrayString actionChoices;
705  for (int i = 0; i < kNumActions; i++)
706  {
707  actionChoices.Add(wxGetTranslation(kActionStrings[i]));
708  }
709 
710  S.AddSpace(0, 5);
711 
712  S.StartStatic(_("Detect Silence"));
713  {
714  S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
715  {
716  // Threshold
717  mTruncDbChoice = S.AddChoice(_("Level:"), wxT(""), &mDbChoices);
718  mTruncDbChoice->SetValidator(wxGenericValidator(&mTruncDbChoiceIndex));
719  S.SetSizeHints(-1, -1);
720  S.AddSpace(0); // 'choices' already includes units.
721 
722  // Ignored silence
723  FloatingPointValidator<double> vldDur(3, &mInitialAllowedSilence, NUM_VAL_NO_TRAILING_ZEROES);
724  vldDur.SetRange(MIN_Minimum, MAX_Minimum);
725  mInitialAllowedSilenceT = S.AddTextBox(_("Duration:"), wxT(""), 12);
726  mInitialAllowedSilenceT->SetValidator(vldDur);
727  S.AddUnits(_("seconds"));
728  }
729  S.EndMultiColumn();
730  }
731  S.EndStatic();
732 
733  S.StartStatic(_("Action"));
734  {
735  S.StartHorizontalLay();
736  {
737  // Action choices
738  mActionChoice = S.AddChoice( {}, wxT(""), &actionChoices);
739  mActionChoice->SetValidator(wxGenericValidator(&mActionIndex));
740  S.SetSizeHints(-1, -1);
741  }
742  S.EndHorizontalLay();
743  S.StartMultiColumn(3, wxALIGN_CENTER_HORIZONTAL);
744  {
745  // Truncation / Compression factor
746 
747  FloatingPointValidator<double> vldTrunc(3, &mTruncLongestAllowedSilence, NUM_VAL_NO_TRAILING_ZEROES);
748  vldTrunc.SetRange(MIN_Truncate, MAX_Truncate);
749  mTruncLongestAllowedSilenceT = S.AddTextBox(_("Truncate to:"), wxT(""), 12);
750  mTruncLongestAllowedSilenceT->SetValidator(vldTrunc);
751  S.AddUnits(_("seconds"));
752 
753  FloatingPointValidator<double> vldComp(3, &mSilenceCompressPercent, NUM_VAL_NO_TRAILING_ZEROES);
754  vldComp.SetRange(MIN_Compress, MAX_Compress);
755  mSilenceCompressPercentT = S.AddTextBox(_("Compress to:"), wxT(""), 12);
756  mSilenceCompressPercentT->SetValidator(vldComp);
757  S.AddUnits(_("%"));
758  }
759  S.EndMultiColumn();
760 
761  S.StartMultiColumn(2, wxALIGN_CENTER_HORIZONTAL);
762  {
763  mIndependent = S.AddCheckBox(_("Truncate tracks independently"),
764  mbIndependent ? wxT("true") : wxT("false"));
765  }
766  S.EndMultiColumn();
767 }
768  S.EndStatic();
769 
770  UpdateUI();
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 /*
928 void 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 
959 void EffectTruncSilence::OnControlChange(wxCommandEvent & WXUNUSED(evt))
960 {
961  mActionChoice->GetValidator()->TransferFromWindow();
962 
963  UpdateUI();
964 
965  if (!EnableApply(mUIParent->TransferDataFromWindow()))
966  {
967  return;
968  }
969 }
static const wxString * GetDbChoices()
Definition: Shuttle.cpp:87
#define TRUNCATESILENCE_PLUGIN_SYMBOL
Definition: TruncSilence.h:32
double mT1
Definition: Effect.h:460
const double detectFrac
A list of TrackListNode items.
Definition: Track.h:553
bool SaveUserPreset(const wxString &name) override
Definition: Effect.cpp:615
int MessageBox(const wxString &message, long style=DefaultMessageBoxStyle, const wxString &titleStr=wxString{})
Definition: Effect.cpp:2655
bool TotalProgress(double frac)
Definition: Effect.cpp:1970
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:366
wxChoice * mTruncDbChoice
Definition: TruncSilence.h:110
static const double Db2Signal[]
Definition: Shuttle.h:21
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true) const
Definition: WaveTrack.cpp:1950
wxString GetCurrentSettingsGroup() override
Definition: Effect.cpp:814
void CopyInputTracks()
Definition: Effect.cpp:2029
static const int NumDbChoices
Definition: Shuttle.h:20
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: Effect.cpp:2160
wxString GetDescription() override
Track * Next(bool skiplinked=false) override
Definition: Track.cpp:653
void EndMultiColumn()
double CalcPreviewInputLength(double previewLength) override
void PopulateOrExchange(ShuttleGui &S) override
#define XO(s)
Definition: Internat.h:30
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:672
#define ReadAndVerifyEnum(name, list)
Definition: Effect.h:781
bool Process() override
Track * StartWith(Track *val) override
Definition: Track.cpp:492
TrackList * inputTracks() const
Definition: Effect.h:457
virtual ~EffectTruncSilence()
bool GetAutomationParameters(EffectAutomationParameters &parms) override
void EndHorizontalLay()
Definition: ShuttleGui.cpp:975
Track * Next(bool skiplinked=false) override
Definition: Track.cpp:513
void AddUnits(const wxString &Prompt)
Left aligned text string.
Definition: ShuttleGui.cpp:229
Param(DbIndex, int, wxT("Db"), 0, 0, Enums::NumDbChoices-1, 1)
void SetSizeHints(int minX=-1, int minY=-1)
bool Startup() override
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:493
wxCheckBox * AddCheckBox(const wxString &Prompt, const wxString &Selected)
Definition: ShuttleGui.cpp:267
bool DoRemoval(const RegionList &silences, unsigned iGroup, unsigned nGroups, Track *firstTrack, Track *lastTrack, double &totalCutLen)
bool Analyze(RegionList &silenceList, RegionList &trackSilences, const WaveTrack *wt, sampleCount *silentFrame, sampleCount *index, int whichTrack, double *inputLength=NULL, double *minInputLength=NULL)
wxChoice * mActionChoice
Definition: TruncSilence.h:111
kActions
void Set(samplePtr buffer, sampleFormat format, sampleCount start, size_t len)
Definition: WaveTrack.cpp:2027
wxTextCtrl * mTruncLongestAllowedSilenceT
Definition: TruncSilence.h:113
bool FindSilences(RegionList &silences, TrackList *list, Track *firstTrack, Track *lastTrack)
wxArrayString mDbChoices
Definition: TruncSilence.h:106
wxString ManualPage() override
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:966
Track * Last(bool skiplinked=false) override
Definition: Track.cpp:536
double mTruncLongestAllowedSilence
Definition: TruncSilence.h:102
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:998
wxChoice * AddChoice(const wxString &Prompt, const wxString &Selected, const wxArrayString *pChoices)
Definition: ShuttleGui.cpp:331
wxCheckBox * mIndependent
Definition: TruncSilence.h:115
Structure to hold region of a wavetrack and a comparison function for sortability.
Definition: WaveTrack.h:42
#define Region
Definition: VSTControlGTK.h:15
void Intersect(RegionList &dest, const RegionList &src)
EffectType GetType() override
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
double start
Definition: WaveTrack.h:47
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:67
#define ReadAndVerifyDouble(name)
Definition: Effect.h:787
int min(int a, int b)
static const size_t DEF_BlendFrameCount
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:1604
bool SetAutomationParameters(EffectAutomationParameters &parms) override
wxTextCtrl * mInitialAllowedSilenceT
Definition: TruncSilence.h:112
wxWindow * mUIParent
Definition: Effect.h:471
static const wxString DbChoices[]
Definition: Shuttle.h:22
An iterator for a TrackList.
Definition: Track.h:339
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
wxString GetSymbol() override
double end
Definition: WaveTrack.h:47
int GetNumWaveTracks()
Definition: Effect.h:343
Track * GetLink() const
Definition: Track.cpp:204
bool IsSyncLocked()
Definition: Project.cpp:5461
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: WaveTrack.cpp:1822
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:302
static const wxChar * kActionStrings[kNumActions]
wxStaticBox * StartStatic(const wxString &Str, int iProp=0)
Definition: ShuttleGui.cpp:701
#define ReadAndVerifyBool(name)
Definition: Effect.h:789
Track * First(TrackList *val=NULL) override
Definition: Track.cpp:502
double mSilenceCompressPercent
Definition: TruncSilence.h:103
wxTextCtrl * mSilenceCompressPercentT
Definition: TruncSilence.h:114
double mInitialAllowedSilence
Definition: TruncSilence.h:101
double GetEndTime() const
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1852
Truncate Silence automatically reduces the length of passages where the volume is below a set thresho...
Definition: TruncSilence.h:36
END_EVENT_TABLE()
wxSizerItem * AddSpace(int width, int height)
double GetRate() const
Definition: WaveTrack.cpp:397
bool TransferDataToWindow() override
double LongSamplesToTime(sampleCount pos) const
Convert correctly between an number of samples and an (absolute) time in seconds. ...
Definition: WaveTrack.cpp:1827
Track * StartWith(Track *member) override
Definition: Track.cpp:606
void OnControlChange(wxCommandEvent &evt)
std::shared_ptr< TrackList > mOutputTracks
Definition: Effect.h:458
bool TransferDataFromWindow() override
virtual bool EnableApply(bool enable=true)
Definition: Effect.cpp:1879
double mT0
Definition: Effect.h:459
static const double DEF_MinTruncMs