Audacity 3.2.0
AutoDuck.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 AutoDuck.cpp
6
7 Markus Meyer
8
9*******************************************************************/
18#include "AutoDuck.h"
19#include "BasicUI.h"
20#include "EffectEditor.h"
21#include "EffectOutputTracks.h"
22#include "LoadEffects.h"
23#include "UserException.h"
24
25#include <math.h>
26
27#include <wx/dcclient.h>
28#include <wx/dcmemory.h>
29
30#include "AColor.h"
31#include "AllThemeResources.h"
32#include "Prefs.h"
33#include "ShuttleGui.h"
34#include "Theme.h"
35#include "../widgets/valnum.h"
36
37#include "WaveClip.h"
38#include "WaveTrack.h"
39#include "TimeStretching.h"
40#include "AudacityMessageBox.h"
41
43{
47 > parameters;
48 return parameters;
49}
50
51/*
52 * Common constants
53 */
54
55static const size_t kBufSize = 131072u; // number of samples to process at once
56static const size_t kRMSWindowSize = 100u; // samples in circular RMS window buffer
57
58/*
59 * A auto duck region and an array of auto duck regions
60 */
61
63{
64 AutoDuckRegion(double t0, double t1)
65 {
66 this->t0 = t0;
67 this->t1 = t1;
68 }
69
70 double t0;
71 double t1;
72};
73
74/*
75 * Effect implementation
76 */
77
79{ XO("Auto Duck") };
80
82
83BEGIN_EVENT_TABLE(EffectAutoDuck, wxEvtHandler)
84 EVT_TEXT(wxID_ANY, EffectAutoDuck::OnValueChanged)
86
88{
89 Parameters().Reset(*this);
90 SetLinearEffectFlag(true);
91}
92
94{
95}
96
97// ComponentInterface implementation
98
100{
101 return Symbol;
102}
103
105{
106 return XO("Reduces (ducks) the volume of one or more tracks whenever the volume of a specified \"control\" track reaches a particular level");
107}
108
110{
111 return L"Auto_Duck";
112}
113
114// EffectDefinitionInterface implementation
115
117{
118 return EffectTypeProcess;
119}
120
121// Effect implementation
122
124{
125 mControlTrack = nullptr;
126
127 // Find the control track, which is the non-selected wave track immediately
128 // after the last selected wave track. Fail if there is no such track or if
129 // any selected track is not a wave track.
130 bool lastWasSelectedWaveTrack = false;
131 const WaveTrack *controlTrackCandidate = nullptr;
132 for (auto t : *inputTracks()) {
133 if (lastWasSelectedWaveTrack && !t->GetSelected())
134 // This could be the control track, so remember it
135 controlTrackCandidate = dynamic_cast<const WaveTrack *>(t);
136
137 lastWasSelectedWaveTrack = false;
138 if (t->GetSelected()) {
139 bool ok = t->TypeSwitch<bool>(
140 [&](const WaveTrack &) {
141 lastWasSelectedWaveTrack = true;
142 controlTrackCandidate = nullptr;
143 return true;
144 },
145 [&](const Track &) {
147 /* i18n-hint: Auto duck is the name of an effect that 'ducks'
148 (reduces the volume) of the audio automatically when there is
149 sound on another track. Not as in 'Donald-Duck'!*/
150 XO("You selected a track which does not contain audio. "
151 "AutoDuck can only process audio tracks."),
152 wxICON_ERROR);
153 return false;
154 }
155 );
156 if (!ok)
157 return false;
158 }
159 }
160
161 if (!controlTrackCandidate) {
163 /* i18n-hint: Auto duck is the name of an effect that 'ducks' (reduces
164 the volume) of the audio automatically when there is sound on another
165 track. Not as in 'Donald-Duck'!*/
166 XO("Auto Duck needs a control track which must be placed below the "
167 "selected track(s)."),
168 wxICON_ERROR);
169 return false;
170 }
171
172 mControlTrack = controlTrackCandidate;
173 return true;
174}
175
177{
178 if (GetNumWaveTracks() == 0 || !mControlTrack)
179 return false;
180
181 bool cancel = false;
182
183 auto start =
185 auto end =
187
188 if (end <= start)
189 return false;
190
191 WaveTrack::Holder pFirstTrack;
192 auto pControlTrack = mControlTrack;
193 // If there is any stretch in the control track, substitute a temporary
194 // rendering before trying to use GetFloats
195 {
196 const auto t0 = pControlTrack->LongSamplesToTime(start);
197 const auto t1 = pControlTrack->LongSamplesToTime(end);
198 if (TimeStretching::HasPitchOrSpeed(*pControlTrack, t0, t1)) {
199 pFirstTrack = pControlTrack->Duplicate()->SharedPointer<WaveTrack>();
200 if (pFirstTrack) {
202 [&](const ProgressReporter& reportProgress) {
203 pFirstTrack->ApplyPitchAndSpeed(
204 { { t0, t1 } }, reportProgress);
205 },
207 XO("Rendering Control-Track Time-Stretched Audio"));
208 pControlTrack = pFirstTrack.get();
209 }
210 }
211 }
212
213 // the minimum number of samples we have to wait until the maximum
214 // pause has been exceeded
215 double maxPause = mMaximumPause;
216
217 // We don't fade in until we have time enough to actually fade out again
218 if (maxPause < mOuterFadeDownLen + mOuterFadeUpLen)
220
221 auto minSamplesPause =
222 pControlTrack->TimeToLongSamples(maxPause);
223
224 double threshold = DB_TO_LINEAR(mThresholdDb);
225
226 // adjust the threshold so we can compare it to the rmsSum value
227 threshold = threshold * threshold * kRMSWindowSize;
228
229 int rmsPos = 0;
230 double rmsSum = 0;
231 // to make the progress bar appear more natural, we first look for all
232 // duck regions and apply them all at once afterwards
233 std::vector<AutoDuckRegion> regions;
234 bool inDuckRegion = false;
235 {
236 Floats rmsWindow{ kRMSWindowSize, true };
237
238 Floats buf{ kBufSize };
239
240 // initialize the following two variables to prevent compiler warning
241 double duckRegionStart = 0;
242 sampleCount curSamplesPause = 0;
243
244 auto pos = start;
245
246 const auto pControlChannel = *pControlTrack->Channels().begin();
247 while (pos < end)
248 {
249 const auto len = limitSampleBufferSize( kBufSize, end - pos );
250
251 pControlChannel->GetFloats(buf.get(), pos, len);
252
253 for (auto i = pos; i < pos + len; i++)
254 {
255 rmsSum -= rmsWindow[rmsPos];
256 // i - pos is bounded by len:
257 auto index = ( i - pos ).as_size_t();
258 rmsWindow[rmsPos] = buf[ index ] * buf[ index ];
259 rmsSum += rmsWindow[rmsPos];
260 rmsPos = (rmsPos + 1) % kRMSWindowSize;
261
262 bool thresholdExceeded = rmsSum > threshold;
263
264 if (thresholdExceeded)
265 {
266 // everytime the threshold is exceeded, reset our count for
267 // the number of pause samples
268 curSamplesPause = 0;
269
270 if (!inDuckRegion)
271 {
272 // the threshold has been exceeded for the first time, so
273 // let the duck region begin here
274 inDuckRegion = true;
275 duckRegionStart = pControlTrack->LongSamplesToTime(i);
276 }
277 }
278
279 if (!thresholdExceeded && inDuckRegion)
280 {
281 // the threshold has not been exceeded and we are in a duck
282 // region, but only fade in if the maximum pause has been
283 // exceeded
284 curSamplesPause += 1;
285
286 if (curSamplesPause >= minSamplesPause)
287 {
288 // do the actual duck fade and reset all values
289 double duckRegionEnd =
290 pControlTrack->LongSamplesToTime(i - curSamplesPause);
291
292 regions.push_back(AutoDuckRegion(
293 duckRegionStart - mOuterFadeDownLen,
294 duckRegionEnd + mOuterFadeUpLen));
295
296 inDuckRegion = false;
297 }
298 }
299 }
300
301 pos += len;
302
303 if (TotalProgress(
304 (pos - start).as_double() /
305 (end - start).as_double() /
306 (GetNumWaveTracks() + 1)
307 ))
308 {
309 cancel = true;
310 break;
311 }
312 }
313
314 // apply last duck fade, if any
315 if (inDuckRegion)
316 {
317 double duckRegionEnd =
318 pControlTrack->LongSamplesToTime(end - curSamplesPause);
319 regions.push_back(AutoDuckRegion(
320 duckRegionStart - mOuterFadeDownLen,
321 duckRegionEnd + mOuterFadeUpLen));
322 }
323 }
324
325 if (!cancel) {
326 EffectOutputTracks outputs { *mTracks, GetType(), { { mT0, mT1 } } };
327
328 int trackNum = 0;
329
330 for (auto iterTrack : outputs.Get().Selected<WaveTrack>()) {
331 for (const auto pChannel : iterTrack->Channels())
332 for (size_t i = 0; i < regions.size(); ++i) {
333 const AutoDuckRegion& region = regions[i];
334 if (ApplyDuckFade(trackNum++, *pChannel, region.t0, region.t1)) {
335 cancel = true;
336 goto done;
337 }
338 }
339
340 done:
341 if (cancel)
342 break;
343 }
344
345 if (!cancel)
346 outputs.Commit();
347 }
348
349 return !cancel;
350}
351
352std::unique_ptr<EffectEditor> EffectAutoDuck::PopulateOrExchange(
354 const EffectOutputs *)
355{
356 mUIParent = S.GetParent();
357 S.SetBorder(5);
358 S.StartVerticalLay(true);
359 {
360 S.AddSpace(0, 5);
361
362 mPanel = safenew EffectAutoDuck::Panel(S.GetParent(), wxID_ANY, this);
363 S.AddWindow(mPanel);
364
365 S.AddSpace(0, 5);
366
367 S.StartMultiColumn(6, wxCENTER);
368 {
369 mDuckAmountDbBox = S.Validator<FloatingPointValidator<double>>(
370 1, &mDuckAmountDb, NumValidatorStyle::NO_TRAILING_ZEROES,
372 .NameSuffix(XO("db"))
373 .AddTextBox(XXO("Duck &amount:"), wxT(""), 10);
374 S.AddUnits(XO("dB"));
375
376 mMaximumPauseBox = S.Validator<FloatingPointValidator<double>>(
377 2, &mMaximumPause, NumValidatorStyle::NO_TRAILING_ZEROES,
379 .NameSuffix(XO("seconds"))
380 .AddTextBox(XXO("Ma&ximum pause:"), wxT(""), 10);
381 S.AddUnits(XO("seconds"));
382
383 mOuterFadeDownLenBox = S.Validator<FloatingPointValidator<double>>(
384 2, &mOuterFadeDownLen, NumValidatorStyle::NO_TRAILING_ZEROES,
386 .NameSuffix(XO("seconds"))
387 .AddTextBox(XXO("Outer fade &down length:"), wxT(""), 10);
388 S.AddUnits(XO("seconds"));
389
390 mOuterFadeUpLenBox = S.Validator<FloatingPointValidator<double>>(
391 2, &mOuterFadeUpLen, NumValidatorStyle::NO_TRAILING_ZEROES,
393 .NameSuffix(XO("seconds"))
394 .AddTextBox(XXO("Outer fade &up length:"), wxT(""), 10);
395 S.AddUnits(XO("seconds"));
396
397 mInnerFadeDownLenBox = S.Validator<FloatingPointValidator<double>>(
398 2, &mInnerFadeDownLen, NumValidatorStyle::NO_TRAILING_ZEROES,
400 .NameSuffix(XO("seconds"))
401 .AddTextBox(XXO("Inner fade d&own length:"), wxT(""), 10);
402 S.AddUnits(XO("seconds"));
403
404 mInnerFadeUpLenBox = S.Validator<FloatingPointValidator<double>>(
405 2, &mInnerFadeUpLen, NumValidatorStyle::NO_TRAILING_ZEROES,
407 .NameSuffix(XO("seconds"))
408 .AddTextBox(XXO("Inner &fade up length:"), wxT(""), 10);
409 S.AddUnits(XO("seconds"));
410 }
411 S.EndMultiColumn();
412
413 S.StartMultiColumn(3, wxCENTER);
414 {
415 mThresholdDbBox = S.Validator<FloatingPointValidator<double>>(
416 2, &mThresholdDb, NumValidatorStyle::NO_TRAILING_ZEROES,
418 .NameSuffix(XO("db"))
419 .AddTextBox(XXO("&Threshold:"), wxT(""), 10);
420 S.AddUnits(XO("dB"));
421 }
422 S.EndMultiColumn();
423
424 }
425 S.EndVerticalLay();
426
427 return nullptr;
428}
429
431{
432 return DoTransferDataToWindow();
433}
434
436{
437 // Issue 2324: don't remove these two lines
438 if (!mUIParent->TransferDataToWindow())
439 return false;
440
441 mPanel->Refresh(false);
442
443 return true;
444}
445
447{
448 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
449 {
450 return false;
451 }
452
453 return true;
454}
455
456// EffectAutoDuck implementation
457
458// this currently does an exponential fade
460 double t0, double t1)
461{
462 bool cancel = false;
463
464 auto start = track.TimeToLongSamples(t0);
465 auto end = track.TimeToLongSamples(t1);
466
467 Floats buf{ kBufSize };
468 auto pos = start;
469
470 auto fadeDownSamples = track.TimeToLongSamples(
472 if (fadeDownSamples < 1)
473 fadeDownSamples = 1;
474
475 auto fadeUpSamples = track.TimeToLongSamples(
477 if (fadeUpSamples < 1)
478 fadeUpSamples = 1;
479
480 float fadeDownStep = mDuckAmountDb / fadeDownSamples.as_double();
481 float fadeUpStep = mDuckAmountDb / fadeUpSamples.as_double();
482
483 while (pos < end) {
484 const auto len = limitSampleBufferSize(kBufSize, end - pos);
485 track.GetFloats(buf.get(), pos, len);
486 for (auto i = pos; i < pos + len; ++i) {
487 float gainDown = fadeDownStep * (i - start).as_float();
488 float gainUp = fadeUpStep * (end - i).as_float();
489
490 float gain;
491 if (gainDown > gainUp)
492 gain = gainDown;
493 else
494 gain = gainUp;
495 if (gain < mDuckAmountDb)
496 gain = mDuckAmountDb;
497
498 // i - pos is bounded by len:
499 buf[ ( i - pos ).as_size_t() ] *= DB_TO_LINEAR(gain);
500 }
501
502 if (!track.SetFloats(buf.get(), pos, len)) {
503 cancel = true;
504 break;
505 }
506
507 pos += len;
508
509 float curTime = track.LongSamplesToTime(pos);
510 float fractionFinished = (curTime - mT0) / (mT1 - mT0);
511 if (TotalProgress((trackNum + 1 + fractionFinished) /
512 (GetNumWaveTracks() + 1))
513 ) {
514 cancel = true;
515 break;
516 }
517 }
518
519 return cancel;
520}
521
522void EffectAutoDuck::OnValueChanged(wxCommandEvent & WXUNUSED(evt))
523{
524 mPanel->Refresh(false);
525}
526
527/*
528 * EffectAutoDuck::Panel implementation
529 */
530
531#define CONTROL_POINT_REGION 10 // pixel distance to click on a control point
532#define CONTROL_POINT_MIN_MOVE 5 // min mouse move until value is changed
533
534#define TEXT_DISTANCE 15 // pixel distance text <-> center of control point
535
536#define FADE_DOWN_START 150 // x coordinate
537#define FADE_UP_START 450 // x coordinate
538#define DUCK_AMOUNT_START 50 // y coordinate
539
540#define FADE_SCALE 40 // scale factor for second -> pixel conversion
541#define DUCK_AMOUNT_SCALE 8 // scale factor for db -> pixel conversion
542
543static int GetDistance(const wxPoint& first, const wxPoint& second)
544{
545 int distanceX = abs(first.x - second.x);
546 int distanceY = abs(first.y - second.y);
547 if (distanceX > distanceY)
548 return distanceX;
549 else
550 return distanceY;
551}
552
553BEGIN_EVENT_TABLE(EffectAutoDuck::Panel, wxPanelWrapper)
555 EVT_MOUSE_CAPTURE_CHANGED(EffectAutoDuck::Panel::OnMouseCaptureChanged)
556 EVT_MOUSE_CAPTURE_LOST(EffectAutoDuck::Panel::OnMouseCaptureLost)
561
563 wxWindow *parent, wxWindowID winid, EffectAutoDuck *effect)
564: wxPanelWrapper(parent, winid, wxDefaultPosition, wxSize(600, 300))
565{
566 mParent = parent;
567 mEffect = effect;
568 mCurrentControlPoint = none;
569 mBackgroundBitmap = NULL;
570
571 ResetControlPoints();
572}
573
575{
576 if(HasCapture())
577 ReleaseMouse();
578}
579
581{
582 mControlPoints[innerFadeDown] = wxPoint(-100,-100);
583 mControlPoints[innerFadeUp] = wxPoint(-100,-100);
584 mControlPoints[outerFadeDown] = wxPoint(-100,-100);
585 mControlPoints[outerFadeUp] = wxPoint(-100,-100);
586 mControlPoints[duckAmount] = wxPoint(-100,-100);
587}
588
589void EffectAutoDuck::Panel::OnPaint(wxPaintEvent & WXUNUSED(evt))
590{
591 int clientWidth, clientHeight;
592 GetSize(&clientWidth, &clientHeight);
593
594 if (!mBackgroundBitmap || mBackgroundBitmap->GetWidth() != clientWidth ||
595 mBackgroundBitmap->GetHeight() != clientHeight)
596 {
597 mBackgroundBitmap = std::make_unique<wxBitmap>(clientWidth, clientHeight,24);
598 }
599
600 wxMemoryDC dc;
601 dc.SelectObject(*mBackgroundBitmap);
602
603 dc.SetBrush(*wxWHITE_BRUSH);
604 dc.SetPen(*wxBLACK_PEN);
605 dc.DrawRectangle(0, 0, clientWidth, clientHeight);
606
607 dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
608 wxFONTWEIGHT_NORMAL));
609 dc.SetTextForeground(*wxBLACK);
610 dc.SetTextBackground(*wxWHITE);
611
612 double duckAmountDb = 0;
613 double innerFadeDownLen = 0;
614 double innerFadeUpLen = 0;
615 double outerFadeDownLen = 0;
616 double outerFadeUpLen = 0;
617 mEffect->mDuckAmountDbBox->GetValue().ToDouble(&duckAmountDb);
618 mEffect->mInnerFadeDownLenBox->GetValue().ToDouble(&innerFadeDownLen);
619 mEffect->mInnerFadeUpLenBox->GetValue().ToDouble(&innerFadeUpLen);
620 mEffect->mOuterFadeDownLenBox->GetValue().ToDouble(&outerFadeDownLen);
621 mEffect->mOuterFadeUpLenBox->GetValue().ToDouble(&outerFadeUpLen);
622
623 if (innerFadeDownLen < InnerFadeDownLen.min || innerFadeDownLen > InnerFadeDownLen.max ||
624 innerFadeUpLen < InnerFadeUpLen.min || innerFadeUpLen > InnerFadeUpLen.max ||
625 outerFadeDownLen < OuterFadeDownLen.min || outerFadeDownLen > OuterFadeDownLen.max ||
626 outerFadeUpLen < OuterFadeUpLen.min || outerFadeUpLen > OuterFadeUpLen.max ||
627 duckAmountDb < DuckAmountDb.min || duckAmountDb > DuckAmountDb.max)
628 {
629 // values are out of range, no preview available
630 wxString message = _("Preview not available");
631 int textWidth = 0, textHeight = 0;
632 dc.GetTextExtent(message, &textWidth, &textHeight);
633 dc.DrawText(message, (clientWidth - textWidth) / 2,
634 (clientHeight - textHeight) / 2);
635
636 ResetControlPoints();
637 } else
638 {
639 // draw preview
640 dc.SetBrush(*wxTRANSPARENT_BRUSH);
641 dc.SetPen(wxPen(theTheme.Colour(clrGraphLines), 3, wxPENSTYLE_SOLID));
642
643 wxPoint points[6];
644
645 points[0].x = 10;
646 points[0].y = DUCK_AMOUNT_START;
647
648 points[1].x = FADE_DOWN_START - (int)(outerFadeDownLen * FADE_SCALE);
649 points[1].y = DUCK_AMOUNT_START;
650
651 points[2].x = FADE_DOWN_START + (int)(innerFadeDownLen * FADE_SCALE);
652 points[2].y = DUCK_AMOUNT_START -
653 (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
654
655 points[3].x = FADE_UP_START - (int)(innerFadeUpLen * FADE_SCALE);
656 points[3].y = DUCK_AMOUNT_START -
657 (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
658
659 points[4].x = FADE_UP_START + (int)(outerFadeUpLen * FADE_SCALE);
660 points[4].y = DUCK_AMOUNT_START;
661
662 points[5].x = clientWidth - 10;
663 points[5].y = DUCK_AMOUNT_START;
664
665 AColor::Lines(dc, 6, points);
666
667 dc.SetPen(wxPen(*wxBLACK, 1, wxPENSTYLE_DOT));
668
669 AColor::Line(dc, FADE_DOWN_START, 10, FADE_DOWN_START, clientHeight - 10);
670 AColor::Line(dc, FADE_UP_START, 10, FADE_UP_START, clientHeight - 10);
671
672 dc.SetPen(AColor::envelopePen);
673 dc.SetBrush(*wxWHITE_BRUSH);
674
675 mControlPoints[outerFadeDown] = points[1];
676 mControlPoints[innerFadeDown] = points[2];
677 mControlPoints[innerFadeUp] = points[3];
678 mControlPoints[outerFadeUp] = points[4];
679 mControlPoints[duckAmount] = wxPoint(
680 (points[2].x + points[3].x) / 2, points[2].y);
681
682 for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
683 {
685 int digits;
686 float value;
687
688 if (cp == innerFadeDown)
689 {
690 value = innerFadeDownLen;
691 digits = 2;
692 }
693 else if (cp == innerFadeUp)
694 {
695 value = innerFadeUpLen;
696 digits = 2;
697 }
698 else if (cp == outerFadeDown)
699 {
700 value = outerFadeDownLen;
701 digits = 2;
702 } else if (cp == outerFadeUp)
703 {
704 value = outerFadeUpLen;
705 digits = 2;
706 }
707 else
708 {
709 value = duckAmountDb;
710 digits = 1;
711 }
712
713 wxString valueStr = Internat::ToDisplayString(value, digits);
714 valueStr += wxT(" ");
715
716 if (cp == duckAmount)
717 /* i18n-hint: short form of 'decibels'.*/
718 valueStr += _("dB");
719 else
720 /* i18n-hint: short form of 'seconds'.*/
721 valueStr += _("s");
722
723 int textWidth = 0, textHeight = 0;
724 GetTextExtent(valueStr, &textWidth, &textHeight);
725
726 int textPosX = mControlPoints[i].x - textWidth / 2;
727 int textPosY = mControlPoints[i].y;
728
729 if (cp == duckAmount || cp == outerFadeDown || cp == outerFadeUp)
730 textPosY -= TEXT_DISTANCE + textHeight;
731 else
732 textPosY += TEXT_DISTANCE;
733
734 dc.DrawText(valueStr, textPosX, textPosY);
735
736 dc.DrawEllipse(mControlPoints[i].x - 3,
737 mControlPoints[i].y - 3, 6, 6);
738 }
739 }
740
741 // copy background buffer to paint dc
742 wxPaintDC paintDC(this);
743 paintDC.Blit(0, 0, clientWidth, clientHeight, &dc, 0, 0);
744
745 // clean up: necessary to free resources on Windows
746 dc.SetPen(wxNullPen);
747 dc.SetBrush(wxNullBrush);
748 dc.SetFont(wxNullFont);
749 dc.SelectObject(wxNullBitmap);
750}
751
753 wxMouseCaptureChangedEvent & WXUNUSED(evt))
754{
755 SetCursor(wxNullCursor);
756 mCurrentControlPoint = none;
757}
758
760 wxMouseCaptureLostEvent & WXUNUSED(evt))
761{
762 mCurrentControlPoint = none;
763
764 if (HasCapture())
765 {
766 ReleaseMouse();
767 }
768}
769
772{
774 int i;
775
776 for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
777 dist[i] = GetDistance(pt, mControlPoints[i]);
778
779 int curMinimum = 0;
780 for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
781 if (dist[i] < dist[curMinimum])
782 curMinimum = i;
783
784 if (dist[curMinimum] <= CONTROL_POINT_REGION)
785 return (EControlPoint)curMinimum;
786 else
787 return none;
788}
789
790void EffectAutoDuck::Panel::OnLeftDown(wxMouseEvent & evt)
791{
792 EControlPoint nearest = GetNearestControlPoint(evt.GetPosition());
793
794 if (nearest != none)
795 {
796 // this control point has been clicked
797 mMouseDownPoint = evt.GetPosition();
798
799 mCurrentControlPoint = nearest;
800 mControlPointMoveActivated = false;
801
802 for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
803 mMoveStartControlPoints[i] = mControlPoints[i];
804
805 if( !HasCapture() )
806 CaptureMouse();
807 }
808}
809
810void EffectAutoDuck::Panel::OnLeftUp(wxMouseEvent & WXUNUSED(evt))
811{
812 if (mCurrentControlPoint != none)
813 {
814 mCurrentControlPoint = none;
815 ReleaseMouse();
816 }
817}
818
819void EffectAutoDuck::Panel::OnMotion(wxMouseEvent & evt)
820{
821 switch (GetNearestControlPoint(evt.GetPosition()))
822 {
823 case none:
824 SetCursor(wxNullCursor);
825 break;
826 case innerFadeDown:
827 case innerFadeUp:
828 case outerFadeDown:
829 case outerFadeUp:
830 SetCursor(wxCursor(wxCURSOR_SIZEWE));
831 break;
832 case duckAmount:
833 SetCursor(wxCursor(wxCURSOR_SIZENS));
834 break;
835 }
836
837 if (mCurrentControlPoint != none)
838 {
839 if (!mControlPointMoveActivated)
840 {
841 int dist;
842
843 if (mCurrentControlPoint == duckAmount)
844 dist = abs(evt.GetY() - mMouseDownPoint.y);
845 else
846 dist = abs(evt.GetX() - mMouseDownPoint.x);
847
848 if (dist >= CONTROL_POINT_MIN_MOVE)
849 mControlPointMoveActivated = true;
850 }
851
852 if (mControlPointMoveActivated)
853 {
854 float newValue;
855
856 switch (mCurrentControlPoint)
857 {
858 case outerFadeDown:
859 newValue = ((double)(FADE_DOWN_START - evt.GetX())) / FADE_SCALE;
860 mEffect->mOuterFadeDownLen = std::clamp<double>(newValue, OuterFadeDownLen.min, OuterFadeDownLen.max);
861 break;
862 case outerFadeUp:
863 newValue = ((double)(evt.GetX() - FADE_UP_START)) / FADE_SCALE;
864 mEffect->mOuterFadeUpLen = std::clamp<double>(newValue, OuterFadeUpLen.min, OuterFadeUpLen.max);
865 break;
866 case innerFadeDown:
867 newValue = ((double)(evt.GetX() - FADE_DOWN_START)) / FADE_SCALE;
868 mEffect->mInnerFadeDownLen = std::clamp<double>(newValue, InnerFadeDownLen.min, InnerFadeDownLen.max);
869 break;
870 case innerFadeUp:
871 newValue = ((double)(FADE_UP_START - evt.GetX())) / FADE_SCALE;
872 mEffect->mInnerFadeUpLen = std::clamp<double>(newValue, InnerFadeUpLen.min, InnerFadeUpLen.max);
873 break;
874 case duckAmount:
875 newValue = ((double)(DUCK_AMOUNT_START - evt.GetY())) / DUCK_AMOUNT_SCALE;
876 mEffect->mDuckAmountDb = std::clamp<double>(newValue, DuckAmountDb.min, DuckAmountDb.max);
877 break;
878 case none:
879 wxASSERT(false); // should not happen
880 }
881 mEffect->DoTransferDataToWindow();
882 Refresh(false);
883 }
884 }
885}
wxT("CloseDown"))
static const size_t kBufSize
Definition: AutoDuck.cpp:55
#define FADE_DOWN_START
Definition: AutoDuck.cpp:536
#define FADE_UP_START
Definition: AutoDuck.cpp:537
#define FADE_SCALE
Definition: AutoDuck.cpp:540
#define CONTROL_POINT_REGION
Definition: AutoDuck.cpp:531
static const size_t kRMSWindowSize
Definition: AutoDuck.cpp:56
static int GetDistance(const wxPoint &first, const wxPoint &second)
Definition: AutoDuck.cpp:543
#define DUCK_AMOUNT_SCALE
Definition: AutoDuck.cpp:541
#define DUCK_AMOUNT_START
Definition: AutoDuck.cpp:538
#define TEXT_DISTANCE
Definition: AutoDuck.cpp:534
#define CONTROL_POINT_MIN_MOVE
Definition: AutoDuck.cpp:532
#define AUTO_DUCK_PANEL_NUM_CONTROL_POINTS
Definition: AutoDuck.h:24
Toolkit-neutral facade for basic user interface services.
END_EVENT_TABLE()
@ none
Definition: Dither.h:20
EffectType
@ EffectTypeProcess
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:9
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:337
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
THEME_API Theme theTheme
Definition: Theme.cpp:82
#define S(N)
Definition: ToChars.cpp:64
std::function< void(double)> ProgressReporter
Definition: Track.h:48
An AudacityException with no visible message.
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:187
static void Lines(wxDC &dc, size_t nPoints, const wxPoint points[])
Definition: AColor.cpp:194
static wxPen envelopePen
Definition: AColor.h:115
Generates EffectParameterMethods overrides from variadic template arguments.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
EControlPoint GetNearestControlPoint(const wxPoint &pt)
Definition: AutoDuck.cpp:771
void OnMotion(wxMouseEvent &evt)
Definition: AutoDuck.cpp:819
void OnLeftDown(wxMouseEvent &evt)
Definition: AutoDuck.cpp:790
void OnMouseCaptureChanged(wxMouseCaptureChangedEvent &evt)
Definition: AutoDuck.cpp:752
void OnLeftUp(wxMouseEvent &evt)
Definition: AutoDuck.cpp:810
void OnPaint(wxPaintEvent &evt)
Definition: AutoDuck.cpp:589
void OnMouseCaptureLost(wxMouseCaptureLostEvent &evt)
Definition: AutoDuck.cpp:759
Implements the Auto Ducking effect.
Definition: AutoDuck.h:27
static constexpr EffectParameter InnerFadeDownLen
Definition: AutoDuck.h:93
double mInnerFadeDownLen
Definition: AutoDuck.h:68
wxTextCtrl * mInnerFadeUpLenBox
Definition: AutoDuck.h:79
ComponentInterfaceSymbol GetSymbol() const override
Definition: AutoDuck.cpp:99
static constexpr EffectParameter InnerFadeUpLen
Definition: AutoDuck.h:95
wxTextCtrl * mInnerFadeDownLenBox
Definition: AutoDuck.h:78
static constexpr EffectParameter OuterFadeDownLen
Definition: AutoDuck.h:97
Panel * mPanel
Definition: AutoDuck.h:86
wxTextCtrl * mOuterFadeDownLenBox
Definition: AutoDuck.h:80
bool Init() override
Definition: AutoDuck.cpp:123
const WaveTrack * mControlTrack
Definition: AutoDuck.h:75
double mThresholdDb
Definition: AutoDuck.h:72
static constexpr EffectParameter MaximumPause
Definition: AutoDuck.h:103
bool DoTransferDataToWindow()
Definition: AutoDuck.cpp:435
wxTextCtrl * mThresholdDbBox
Definition: AutoDuck.h:82
static constexpr EffectParameter ThresholdDb
Definition: AutoDuck.h:101
double mMaximumPause
Definition: AutoDuck.h:73
wxWeakRef< wxWindow > mUIParent
Definition: AutoDuck.h:65
double mDuckAmountDb
Definition: AutoDuck.h:67
const EffectParameterMethods & Parameters() const override
Definition: AutoDuck.cpp:42
double mInnerFadeUpLen
Definition: AutoDuck.h:69
bool ApplyDuckFade(int trackNum, WaveChannel &track, double t0, double t1)
Definition: AutoDuck.cpp:459
bool TransferDataToWindow(const EffectSettings &settings) override
Definition: AutoDuck.cpp:430
virtual ~EffectAutoDuck()
Definition: AutoDuck.cpp:93
void OnValueChanged(wxCommandEvent &evt)
Definition: AutoDuck.cpp:522
static constexpr EffectParameter DuckAmountDb
Definition: AutoDuck.h:91
EffectType GetType() const override
Type determines how it behaves.
Definition: AutoDuck.cpp:116
bool TransferDataFromWindow(EffectSettings &settings) override
Definition: AutoDuck.cpp:446
double mOuterFadeDownLen
Definition: AutoDuck.h:70
static constexpr EffectParameter OuterFadeUpLen
Definition: AutoDuck.h:99
bool Process(EffectInstance &instance, EffectSettings &settings) override
Definition: AutoDuck.cpp:176
wxTextCtrl * mMaximumPauseBox
Definition: AutoDuck.h:83
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
Definition: AutoDuck.cpp:109
double mOuterFadeUpLen
Definition: AutoDuck.h:71
static const ComponentInterfaceSymbol Symbol
Definition: AutoDuck.h:31
TranslatableString GetDescription() const override
Definition: AutoDuck.cpp:104
wxTextCtrl * mOuterFadeUpLenBox
Definition: AutoDuck.h:81
wxTextCtrl * mDuckAmountDbBox
Definition: AutoDuck.h:77
std::unique_ptr< EffectEditor > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) override
Add controls to effect panel; always succeeds.
Definition: AutoDuck.cpp:352
double mT1
Definition: EffectBase.h:114
const TrackList * inputTracks() const
Definition: EffectBase.h:91
std::shared_ptr< TrackList > mTracks
Definition: EffectBase.h:107
double mT0
Definition: EffectBase.h:113
bool TotalProgress(double frac, const TranslatableString &={}) const
Definition: Effect.cpp:335
int GetNumWaveTracks() const
Definition: Effect.h:139
Performs effect computation.
Use this object to copy the input tracks to tentative outputTracks.
Hold values to send to effect output meters.
Interface for manipulations of an Effect's settings.
static int DoMessageBox(const EffectPlugin &plugin, const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={})
static wxString ToDisplayString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, uses the user's locale's decimal separator.
Definition: Internat.cpp:137
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
wxColour & Colour(int iIndex)
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
R TypeSwitch(const Functions &...functions)
Definition: Track.h:381
Holds a msgid for the translation catalog; may also bind format arguments.
static void WithCancellableProgress(std::function< void(const ProgressReporter &)> action, TranslatableString title, TranslatableString message)
A frequently useful convenience wraps a lambda and may throw this type.
bool SetFloats(const float *buffer, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Random-access assignment of a range of samples.
Definition: WaveTrack.h:162
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=FillFormat::fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
"narrow" overload fetches from the unique channel
Definition: WaveTrack.h:129
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
std::shared_ptr< WaveTrack > Holder
Definition: WaveTrack.h:247
double LongSamplesToTime(sampleCount pos) const
sampleCount TimeToLongSamples(double t0) const
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
WAVE_TRACK_API const TranslatableString defaultStretchRenderingTitle
WAVE_TRACK_API bool HasPitchOrSpeed(const WaveTrack &track, double t0, double t1)
BuiltinEffectsModule::Registration< EffectAutoDuck > reg
Definition: AutoDuck.cpp:81
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
a struct that holds a start and end time.
Definition: AutoDuck.cpp:63
AutoDuckRegion(double t0, double t1)
Definition: AutoDuck.cpp:64
const Type min
Minimum value.
const Type max
Maximum value.
Externalized state of a plug-in.