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 "WaveTrackUtilities.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 TrackListHolder tempTracks;
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 auto &clips = pControlTrack->GetClips();
197 const auto t0 = pControlTrack->LongSamplesToTime(start);
198 const auto t1 = pControlTrack->LongSamplesToTime(end);
199 if (WaveTrackUtilities::HasPitchOrSpeed(*pControlTrack, t0, t1)) {
200 tempTracks = pControlTrack->Duplicate();
201 if (tempTracks) {
202 const auto pFirstTrack = *tempTracks->Any<WaveTrack>().begin();
203 if (pFirstTrack)
204 {
206 [&](const ProgressReporter& reportProgress) {
207 pFirstTrack->ApplyPitchAndSpeed(
208 { { t0, t1 } }, reportProgress);
209 },
211 XO("Rendering Control-Track Time-Stretched Audio"));
212 pControlTrack = pFirstTrack;
213 }
214 }
215 }
216 }
217
218 // the minimum number of samples we have to wait until the maximum
219 // pause has been exceeded
220 double maxPause = mMaximumPause;
221
222 // We don't fade in until we have time enough to actually fade out again
223 if (maxPause < mOuterFadeDownLen + mOuterFadeUpLen)
225
226 auto minSamplesPause =
227 pControlTrack->TimeToLongSamples(maxPause);
228
229 double threshold = DB_TO_LINEAR(mThresholdDb);
230
231 // adjust the threshold so we can compare it to the rmsSum value
232 threshold = threshold * threshold * kRMSWindowSize;
233
234 int rmsPos = 0;
235 double rmsSum = 0;
236 // to make the progress bar appear more natural, we first look for all
237 // duck regions and apply them all at once afterwards
238 std::vector<AutoDuckRegion> regions;
239 bool inDuckRegion = false;
240 {
241 Floats rmsWindow{ kRMSWindowSize, true };
242
243 Floats buf{ kBufSize };
244
245 // initialize the following two variables to prevent compiler warning
246 double duckRegionStart = 0;
247 sampleCount curSamplesPause = 0;
248
249 auto pos = start;
250
251 while (pos < end)
252 {
253 const auto len = limitSampleBufferSize( kBufSize, end - pos );
254
255 pControlTrack->GetFloats(buf.get(), pos, len);
256
257 for (auto i = pos; i < pos + len; i++)
258 {
259 rmsSum -= rmsWindow[rmsPos];
260 // i - pos is bounded by len:
261 auto index = ( i - pos ).as_size_t();
262 rmsWindow[rmsPos] = buf[ index ] * buf[ index ];
263 rmsSum += rmsWindow[rmsPos];
264 rmsPos = (rmsPos + 1) % kRMSWindowSize;
265
266 bool thresholdExceeded = rmsSum > threshold;
267
268 if (thresholdExceeded)
269 {
270 // everytime the threshold is exceeded, reset our count for
271 // the number of pause samples
272 curSamplesPause = 0;
273
274 if (!inDuckRegion)
275 {
276 // the threshold has been exceeded for the first time, so
277 // let the duck region begin here
278 inDuckRegion = true;
279 duckRegionStart = pControlTrack->LongSamplesToTime(i);
280 }
281 }
282
283 if (!thresholdExceeded && inDuckRegion)
284 {
285 // the threshold has not been exceeded and we are in a duck
286 // region, but only fade in if the maximum pause has been
287 // exceeded
288 curSamplesPause += 1;
289
290 if (curSamplesPause >= minSamplesPause)
291 {
292 // do the actual duck fade and reset all values
293 double duckRegionEnd =
294 pControlTrack->LongSamplesToTime(i - curSamplesPause);
295
296 regions.push_back(AutoDuckRegion(
297 duckRegionStart - mOuterFadeDownLen,
298 duckRegionEnd + mOuterFadeUpLen));
299
300 inDuckRegion = false;
301 }
302 }
303 }
304
305 pos += len;
306
307 if (TotalProgress(
308 (pos - start).as_double() /
309 (end - start).as_double() /
310 (GetNumWaveTracks() + 1)
311 ))
312 {
313 cancel = true;
314 break;
315 }
316 }
317
318 // apply last duck fade, if any
319 if (inDuckRegion)
320 {
321 double duckRegionEnd =
322 pControlTrack->LongSamplesToTime(end - curSamplesPause);
323 regions.push_back(AutoDuckRegion(
324 duckRegionStart - mOuterFadeDownLen,
325 duckRegionEnd + mOuterFadeUpLen));
326 }
327 }
328
329 if (!cancel) {
330 EffectOutputTracks outputs { *mTracks, GetType(), { { mT0, mT1 } } };
331
332 int trackNum = 0;
333
334 for (auto iterTrack : outputs.Get().Selected<WaveTrack>()) {
335 for (const auto pChannel : iterTrack->Channels())
336 for (size_t i = 0; i < regions.size(); ++i) {
337 const AutoDuckRegion& region = regions[i];
338 if (ApplyDuckFade(trackNum++, *pChannel, region.t0, region.t1)) {
339 cancel = true;
340 goto done;
341 }
342 }
343
344 done:
345 if (cancel)
346 break;
347 }
348
349 if (!cancel)
350 outputs.Commit();
351 }
352
353 return !cancel;
354}
355
356std::unique_ptr<EffectEditor> EffectAutoDuck::PopulateOrExchange(
358 const EffectOutputs *)
359{
360 mUIParent = S.GetParent();
361 S.SetBorder(5);
362 S.StartVerticalLay(true);
363 {
364 S.AddSpace(0, 5);
365
366 mPanel = safenew EffectAutoDuck::Panel(S.GetParent(), wxID_ANY, this);
367 S.AddWindow(mPanel);
368
369 S.AddSpace(0, 5);
370
371 S.StartMultiColumn(6, wxCENTER);
372 {
373 mDuckAmountDbBox = S.Validator<FloatingPointValidator<double>>(
374 1, &mDuckAmountDb, NumValidatorStyle::NO_TRAILING_ZEROES,
376 .NameSuffix(XO("db"))
377 .AddTextBox(XXO("Duck &amount:"), wxT(""), 10);
378 S.AddUnits(XO("dB"));
379
380 mMaximumPauseBox = S.Validator<FloatingPointValidator<double>>(
381 2, &mMaximumPause, NumValidatorStyle::NO_TRAILING_ZEROES,
383 .NameSuffix(XO("seconds"))
384 .AddTextBox(XXO("Ma&ximum pause:"), wxT(""), 10);
385 S.AddUnits(XO("seconds"));
386
387 mOuterFadeDownLenBox = S.Validator<FloatingPointValidator<double>>(
388 2, &mOuterFadeDownLen, NumValidatorStyle::NO_TRAILING_ZEROES,
390 .NameSuffix(XO("seconds"))
391 .AddTextBox(XXO("Outer fade &down length:"), wxT(""), 10);
392 S.AddUnits(XO("seconds"));
393
394 mOuterFadeUpLenBox = S.Validator<FloatingPointValidator<double>>(
395 2, &mOuterFadeUpLen, NumValidatorStyle::NO_TRAILING_ZEROES,
397 .NameSuffix(XO("seconds"))
398 .AddTextBox(XXO("Outer fade &up length:"), wxT(""), 10);
399 S.AddUnits(XO("seconds"));
400
401 mInnerFadeDownLenBox = S.Validator<FloatingPointValidator<double>>(
402 2, &mInnerFadeDownLen, NumValidatorStyle::NO_TRAILING_ZEROES,
404 .NameSuffix(XO("seconds"))
405 .AddTextBox(XXO("Inner fade d&own length:"), wxT(""), 10);
406 S.AddUnits(XO("seconds"));
407
408 mInnerFadeUpLenBox = S.Validator<FloatingPointValidator<double>>(
409 2, &mInnerFadeUpLen, NumValidatorStyle::NO_TRAILING_ZEROES,
411 .NameSuffix(XO("seconds"))
412 .AddTextBox(XXO("Inner &fade up length:"), wxT(""), 10);
413 S.AddUnits(XO("seconds"));
414 }
415 S.EndMultiColumn();
416
417 S.StartMultiColumn(3, wxCENTER);
418 {
419 mThresholdDbBox = S.Validator<FloatingPointValidator<double>>(
420 2, &mThresholdDb, NumValidatorStyle::NO_TRAILING_ZEROES,
422 .NameSuffix(XO("db"))
423 .AddTextBox(XXO("&Threshold:"), wxT(""), 10);
424 S.AddUnits(XO("dB"));
425 }
426 S.EndMultiColumn();
427
428 }
429 S.EndVerticalLay();
430
431 return nullptr;
432}
433
435{
436 return DoTransferDataToWindow();
437}
438
440{
441 // Issue 2324: don't remove these two lines
442 if (!mUIParent->TransferDataToWindow())
443 return false;
444
445 mPanel->Refresh(false);
446
447 return true;
448}
449
451{
452 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
453 {
454 return false;
455 }
456
457 return true;
458}
459
460// EffectAutoDuck implementation
461
462// this currently does an exponential fade
464 double t0, double t1)
465{
466 bool cancel = false;
467
468 auto start = track.TimeToLongSamples(t0);
469 auto end = track.TimeToLongSamples(t1);
470
471 Floats buf{ kBufSize };
472 auto pos = start;
473
474 auto fadeDownSamples = track.TimeToLongSamples(
476 if (fadeDownSamples < 1)
477 fadeDownSamples = 1;
478
479 auto fadeUpSamples = track.TimeToLongSamples(
481 if (fadeUpSamples < 1)
482 fadeUpSamples = 1;
483
484 float fadeDownStep = mDuckAmountDb / fadeDownSamples.as_double();
485 float fadeUpStep = mDuckAmountDb / fadeUpSamples.as_double();
486
487 while (pos < end) {
488 const auto len = limitSampleBufferSize(kBufSize, end - pos);
489 track.GetFloats(buf.get(), pos, len);
490 for (auto i = pos; i < pos + len; ++i) {
491 float gainDown = fadeDownStep * (i - start).as_float();
492 float gainUp = fadeUpStep * (end - i).as_float();
493
494 float gain;
495 if (gainDown > gainUp)
496 gain = gainDown;
497 else
498 gain = gainUp;
499 if (gain < mDuckAmountDb)
500 gain = mDuckAmountDb;
501
502 // i - pos is bounded by len:
503 buf[ ( i - pos ).as_size_t() ] *= DB_TO_LINEAR(gain);
504 }
505
506 if (!track.Set((samplePtr)buf.get(), floatSample, pos, len)) {
507 cancel = true;
508 break;
509 }
510
511 pos += len;
512
513 float curTime = track.LongSamplesToTime(pos);
514 float fractionFinished = (curTime - mT0) / (mT1 - mT0);
515 if (TotalProgress((trackNum + 1 + fractionFinished) /
516 (GetNumWaveTracks() + 1))
517 ) {
518 cancel = true;
519 break;
520 }
521 }
522
523 return cancel;
524}
525
526void EffectAutoDuck::OnValueChanged(wxCommandEvent & WXUNUSED(evt))
527{
528 mPanel->Refresh(false);
529}
530
531/*
532 * EffectAutoDuck::Panel implementation
533 */
534
535#define CONTROL_POINT_REGION 10 // pixel distance to click on a control point
536#define CONTROL_POINT_MIN_MOVE 5 // min mouse move until value is changed
537
538#define TEXT_DISTANCE 15 // pixel distance text <-> center of control point
539
540#define FADE_DOWN_START 150 // x coordinate
541#define FADE_UP_START 450 // x coordinate
542#define DUCK_AMOUNT_START 50 // y coordinate
543
544#define FADE_SCALE 40 // scale factor for second -> pixel conversion
545#define DUCK_AMOUNT_SCALE 8 // scale factor for db -> pixel conversion
546
547static int GetDistance(const wxPoint& first, const wxPoint& second)
548{
549 int distanceX = abs(first.x - second.x);
550 int distanceY = abs(first.y - second.y);
551 if (distanceX > distanceY)
552 return distanceX;
553 else
554 return distanceY;
555}
556
557BEGIN_EVENT_TABLE(EffectAutoDuck::Panel, wxPanelWrapper)
559 EVT_MOUSE_CAPTURE_CHANGED(EffectAutoDuck::Panel::OnMouseCaptureChanged)
560 EVT_MOUSE_CAPTURE_LOST(EffectAutoDuck::Panel::OnMouseCaptureLost)
565
567 wxWindow *parent, wxWindowID winid, EffectAutoDuck *effect)
568: wxPanelWrapper(parent, winid, wxDefaultPosition, wxSize(600, 300))
569{
570 mParent = parent;
571 mEffect = effect;
572 mCurrentControlPoint = none;
573 mBackgroundBitmap = NULL;
574
575 ResetControlPoints();
576}
577
579{
580 if(HasCapture())
581 ReleaseMouse();
582}
583
585{
586 mControlPoints[innerFadeDown] = wxPoint(-100,-100);
587 mControlPoints[innerFadeUp] = wxPoint(-100,-100);
588 mControlPoints[outerFadeDown] = wxPoint(-100,-100);
589 mControlPoints[outerFadeUp] = wxPoint(-100,-100);
590 mControlPoints[duckAmount] = wxPoint(-100,-100);
591}
592
593void EffectAutoDuck::Panel::OnPaint(wxPaintEvent & WXUNUSED(evt))
594{
595 int clientWidth, clientHeight;
596 GetSize(&clientWidth, &clientHeight);
597
598 if (!mBackgroundBitmap || mBackgroundBitmap->GetWidth() != clientWidth ||
599 mBackgroundBitmap->GetHeight() != clientHeight)
600 {
601 mBackgroundBitmap = std::make_unique<wxBitmap>(clientWidth, clientHeight,24);
602 }
603
604 wxMemoryDC dc;
605 dc.SelectObject(*mBackgroundBitmap);
606
607 dc.SetBrush(*wxWHITE_BRUSH);
608 dc.SetPen(*wxBLACK_PEN);
609 dc.DrawRectangle(0, 0, clientWidth, clientHeight);
610
611 dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
612 wxFONTWEIGHT_NORMAL));
613 dc.SetTextForeground(*wxBLACK);
614 dc.SetTextBackground(*wxWHITE);
615
616 double duckAmountDb = 0;
617 double innerFadeDownLen = 0;
618 double innerFadeUpLen = 0;
619 double outerFadeDownLen = 0;
620 double outerFadeUpLen = 0;
621 mEffect->mDuckAmountDbBox->GetValue().ToDouble(&duckAmountDb);
622 mEffect->mInnerFadeDownLenBox->GetValue().ToDouble(&innerFadeDownLen);
623 mEffect->mInnerFadeUpLenBox->GetValue().ToDouble(&innerFadeUpLen);
624 mEffect->mOuterFadeDownLenBox->GetValue().ToDouble(&outerFadeDownLen);
625 mEffect->mOuterFadeUpLenBox->GetValue().ToDouble(&outerFadeUpLen);
626
627 if (innerFadeDownLen < InnerFadeDownLen.min || innerFadeDownLen > InnerFadeDownLen.max ||
628 innerFadeUpLen < InnerFadeUpLen.min || innerFadeUpLen > InnerFadeUpLen.max ||
629 outerFadeDownLen < OuterFadeDownLen.min || outerFadeDownLen > OuterFadeDownLen.max ||
630 outerFadeUpLen < OuterFadeUpLen.min || outerFadeUpLen > OuterFadeUpLen.max ||
631 duckAmountDb < DuckAmountDb.min || duckAmountDb > DuckAmountDb.max)
632 {
633 // values are out of range, no preview available
634 wxString message = _("Preview not available");
635 int textWidth = 0, textHeight = 0;
636 dc.GetTextExtent(message, &textWidth, &textHeight);
637 dc.DrawText(message, (clientWidth - textWidth) / 2,
638 (clientHeight - textHeight) / 2);
639
640 ResetControlPoints();
641 } else
642 {
643 // draw preview
644 dc.SetBrush(*wxTRANSPARENT_BRUSH);
645 dc.SetPen(wxPen(theTheme.Colour(clrGraphLines), 3, wxPENSTYLE_SOLID));
646
647 wxPoint points[6];
648
649 points[0].x = 10;
650 points[0].y = DUCK_AMOUNT_START;
651
652 points[1].x = FADE_DOWN_START - (int)(outerFadeDownLen * FADE_SCALE);
653 points[1].y = DUCK_AMOUNT_START;
654
655 points[2].x = FADE_DOWN_START + (int)(innerFadeDownLen * FADE_SCALE);
656 points[2].y = DUCK_AMOUNT_START -
657 (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
658
659 points[3].x = FADE_UP_START - (int)(innerFadeUpLen * FADE_SCALE);
660 points[3].y = DUCK_AMOUNT_START -
661 (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
662
663 points[4].x = FADE_UP_START + (int)(outerFadeUpLen * FADE_SCALE);
664 points[4].y = DUCK_AMOUNT_START;
665
666 points[5].x = clientWidth - 10;
667 points[5].y = DUCK_AMOUNT_START;
668
669 AColor::Lines(dc, 6, points);
670
671 dc.SetPen(wxPen(*wxBLACK, 1, wxPENSTYLE_DOT));
672
673 AColor::Line(dc, FADE_DOWN_START, 10, FADE_DOWN_START, clientHeight - 10);
674 AColor::Line(dc, FADE_UP_START, 10, FADE_UP_START, clientHeight - 10);
675
676 dc.SetPen(AColor::envelopePen);
677 dc.SetBrush(*wxWHITE_BRUSH);
678
679 mControlPoints[outerFadeDown] = points[1];
680 mControlPoints[innerFadeDown] = points[2];
681 mControlPoints[innerFadeUp] = points[3];
682 mControlPoints[outerFadeUp] = points[4];
683 mControlPoints[duckAmount] = wxPoint(
684 (points[2].x + points[3].x) / 2, points[2].y);
685
686 for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
687 {
689 int digits;
690 float value;
691
692 if (cp == innerFadeDown)
693 {
694 value = innerFadeDownLen;
695 digits = 2;
696 }
697 else if (cp == innerFadeUp)
698 {
699 value = innerFadeUpLen;
700 digits = 2;
701 }
702 else if (cp == outerFadeDown)
703 {
704 value = outerFadeDownLen;
705 digits = 2;
706 } else if (cp == outerFadeUp)
707 {
708 value = outerFadeUpLen;
709 digits = 2;
710 }
711 else
712 {
713 value = duckAmountDb;
714 digits = 1;
715 }
716
717 wxString valueStr = Internat::ToDisplayString(value, digits);
718 valueStr += wxT(" ");
719
720 if (cp == duckAmount)
721 /* i18n-hint: short form of 'decibels'.*/
722 valueStr += _("dB");
723 else
724 /* i18n-hint: short form of 'seconds'.*/
725 valueStr += _("s");
726
727 int textWidth = 0, textHeight = 0;
728 GetTextExtent(valueStr, &textWidth, &textHeight);
729
730 int textPosX = mControlPoints[i].x - textWidth / 2;
731 int textPosY = mControlPoints[i].y;
732
733 if (cp == duckAmount || cp == outerFadeDown || cp == outerFadeUp)
734 textPosY -= TEXT_DISTANCE + textHeight;
735 else
736 textPosY += TEXT_DISTANCE;
737
738 dc.DrawText(valueStr, textPosX, textPosY);
739
740 dc.DrawEllipse(mControlPoints[i].x - 3,
741 mControlPoints[i].y - 3, 6, 6);
742 }
743 }
744
745 // copy background buffer to paint dc
746 wxPaintDC paintDC(this);
747 paintDC.Blit(0, 0, clientWidth, clientHeight, &dc, 0, 0);
748
749 // clean up: necessary to free resources on Windows
750 dc.SetPen(wxNullPen);
751 dc.SetBrush(wxNullBrush);
752 dc.SetFont(wxNullFont);
753 dc.SelectObject(wxNullBitmap);
754}
755
757 wxMouseCaptureChangedEvent & WXUNUSED(evt))
758{
759 SetCursor(wxNullCursor);
760 mCurrentControlPoint = none;
761}
762
764 wxMouseCaptureLostEvent & WXUNUSED(evt))
765{
766 mCurrentControlPoint = none;
767
768 if (HasCapture())
769 {
770 ReleaseMouse();
771 }
772}
773
776{
778 int i;
779
780 for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
781 dist[i] = GetDistance(pt, mControlPoints[i]);
782
783 int curMinimum = 0;
784 for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
785 if (dist[i] < dist[curMinimum])
786 curMinimum = i;
787
788 if (dist[curMinimum] <= CONTROL_POINT_REGION)
789 return (EControlPoint)curMinimum;
790 else
791 return none;
792}
793
794void EffectAutoDuck::Panel::OnLeftDown(wxMouseEvent & evt)
795{
796 EControlPoint nearest = GetNearestControlPoint(evt.GetPosition());
797
798 if (nearest != none)
799 {
800 // this control point has been clicked
801 mMouseDownPoint = evt.GetPosition();
802
803 mCurrentControlPoint = nearest;
804 mControlPointMoveActivated = false;
805
806 for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
807 mMoveStartControlPoints[i] = mControlPoints[i];
808
809 if( !HasCapture() )
810 CaptureMouse();
811 }
812}
813
814void EffectAutoDuck::Panel::OnLeftUp(wxMouseEvent & WXUNUSED(evt))
815{
816 if (mCurrentControlPoint != none)
817 {
818 mCurrentControlPoint = none;
819 ReleaseMouse();
820 }
821}
822
823void EffectAutoDuck::Panel::OnMotion(wxMouseEvent & evt)
824{
825 switch (GetNearestControlPoint(evt.GetPosition()))
826 {
827 case none:
828 SetCursor(wxNullCursor);
829 break;
830 case innerFadeDown:
831 case innerFadeUp:
832 case outerFadeDown:
833 case outerFadeUp:
834 SetCursor(wxCursor(wxCURSOR_SIZEWE));
835 break;
836 case duckAmount:
837 SetCursor(wxCursor(wxCURSOR_SIZENS));
838 break;
839 }
840
841 if (mCurrentControlPoint != none)
842 {
843 if (!mControlPointMoveActivated)
844 {
845 int dist;
846
847 if (mCurrentControlPoint == duckAmount)
848 dist = abs(evt.GetY() - mMouseDownPoint.y);
849 else
850 dist = abs(evt.GetX() - mMouseDownPoint.x);
851
852 if (dist >= CONTROL_POINT_MIN_MOVE)
853 mControlPointMoveActivated = true;
854 }
855
856 if (mControlPointMoveActivated)
857 {
858 float newValue;
859
860 switch (mCurrentControlPoint)
861 {
862 case outerFadeDown:
863 newValue = ((double)(FADE_DOWN_START - evt.GetX())) / FADE_SCALE;
864 mEffect->mOuterFadeDownLen = std::clamp<double>(newValue, OuterFadeDownLen.min, OuterFadeDownLen.max);
865 break;
866 case outerFadeUp:
867 newValue = ((double)(evt.GetX() - FADE_UP_START)) / FADE_SCALE;
868 mEffect->mOuterFadeUpLen = std::clamp<double>(newValue, OuterFadeUpLen.min, OuterFadeUpLen.max);
869 break;
870 case innerFadeDown:
871 newValue = ((double)(evt.GetX() - FADE_DOWN_START)) / FADE_SCALE;
872 mEffect->mInnerFadeDownLen = std::clamp<double>(newValue, InnerFadeDownLen.min, InnerFadeDownLen.max);
873 break;
874 case innerFadeUp:
875 newValue = ((double)(FADE_UP_START - evt.GetX())) / FADE_SCALE;
876 mEffect->mInnerFadeUpLen = std::clamp<double>(newValue, InnerFadeUpLen.min, InnerFadeUpLen.max);
877 break;
878 case duckAmount:
879 newValue = ((double)(DUCK_AMOUNT_START - evt.GetY())) / DUCK_AMOUNT_SCALE;
880 mEffect->mDuckAmountDb = std::clamp<double>(newValue, DuckAmountDb.min, DuckAmountDb.max);
881 break;
882 case none:
883 wxASSERT(false); // should not happen
884 }
885 mEffect->DoTransferDataToWindow();
886 Refresh(false);
887 }
888 }
889}
wxT("CloseDown"))
static const size_t kBufSize
Definition: AutoDuck.cpp:55
#define FADE_DOWN_START
Definition: AutoDuck.cpp:540
#define FADE_UP_START
Definition: AutoDuck.cpp:541
#define FADE_SCALE
Definition: AutoDuck.cpp:544
#define CONTROL_POINT_REGION
Definition: AutoDuck.cpp:535
static const size_t kRMSWindowSize
Definition: AutoDuck.cpp:56
static int GetDistance(const wxPoint &first, const wxPoint &second)
Definition: AutoDuck.cpp:547
#define DUCK_AMOUNT_SCALE
Definition: AutoDuck.cpp:545
#define DUCK_AMOUNT_START
Definition: AutoDuck.cpp:542
#define TEXT_DISTANCE
Definition: AutoDuck.cpp:538
#define CONTROL_POINT_MIN_MOVE
Definition: AutoDuck.cpp:536
#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:335
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
char * samplePtr
Definition: SampleFormat.h:57
THEME_API Theme theTheme
Definition: Theme.cpp:82
#define S(N)
Definition: ToChars.cpp:64
std::function< void(double)> ProgressReporter
Definition: Track.h:53
std::shared_ptr< TrackList > TrackListHolder
Definition: Track.h:42
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:775
void OnMotion(wxMouseEvent &evt)
Definition: AutoDuck.cpp:823
void OnLeftDown(wxMouseEvent &evt)
Definition: AutoDuck.cpp:794
void OnMouseCaptureChanged(wxMouseCaptureChangedEvent &evt)
Definition: AutoDuck.cpp:756
void OnLeftUp(wxMouseEvent &evt)
Definition: AutoDuck.cpp:814
void OnPaint(wxPaintEvent &evt)
Definition: AutoDuck.cpp:593
void OnMouseCaptureLost(wxMouseCaptureLostEvent &evt)
Definition: AutoDuck.cpp:763
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:439
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:463
bool TransferDataToWindow(const EffectSettings &settings) override
Definition: AutoDuck.cpp:434
virtual ~EffectAutoDuck()
Definition: AutoDuck.cpp:93
void OnValueChanged(wxCommandEvent &evt)
Definition: AutoDuck.cpp:526
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:450
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:356
double mT1
Definition: EffectBase.h:116
const TrackList * inputTracks() const
Definition: EffectBase.h:91
std::shared_ptr< TrackList > mTracks
Definition: EffectBase.h:109
double mT0
Definition: EffectBase.h:115
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:122
R TypeSwitch(const Functions &...functions)
Definition: Track.h:436
Holds a msgid for the translation catalog; may also bind format arguments.
bool Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Random-access assignment of a range of samples.
Definition: WaveTrack.cpp:3560
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:159
A Track that contains audio waveform data.
Definition: WaveTrack.h:227
WaveClipHolders & GetClips()
Definition: WaveTrack.h:699
double LongSamplesToTime(sampleCount pos) const
sampleCount TimeToLongSamples(double t0) const
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
WAVE_TRACK_API const TranslatableString defaultStretchRenderingTitle
WAVE_TRACK_API bool HasPitchOrSpeed(const WaveTrack &track, double t0, double t1)
WAVE_TRACK_API void WithClipRenderingProgress(std::function< void(const ProgressReporter &)> action, TranslatableString title=defaultStretchRenderingTitle, TranslatableString message=XO("Rendering Clip"))
BuiltinEffectsModule::Registration< EffectAutoDuck > reg
Definition: AutoDuck.cpp:81
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.