1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   TimeScale.cpp
6 
7   Clayton Otey
8 
9 *******************************************************************//**
10 
11 \class EffectTimeScale
12 \brief An EffectTimeScale does high quality sliding time scaling/pitch shifting
13 
14 *//*******************************************************************/
15 
16 
17 
18 #if USE_SBSMS
19 #include "TimeScale.h"
20 #include "LoadEffects.h"
21 
22 #include <math.h>
23 
24 #include <wx/intl.h>
25 #include <wx/slider.h>
26 
27 #include "../Shuttle.h"
28 #include "../ShuttleGui.h"
29 #include "../widgets/valnum.h"
30 
31 enum
32 {
33    ID_RatePercentChangeStart = 10000,
34    ID_RatePercentChangeEnd,
35    ID_PitchHalfStepsStart,
36    ID_PitchHalfStepsEnd,
37    ID_PitchPercentChangeStart,
38    ID_PitchPercentChangeEnd
39 };
40 
41 // Define keys, defaults, minimums, and maximums for the effect parameters
42 //
43 //     Name                Type    Key                            Def   Min      Max    Scale
44 Param( RatePercentStart,   double, wxT("RatePercentChangeStart"),  0.0,  -90.0,   500,   1  );
45 Param( RatePercentEnd,     double, wxT("RatePercentChangeEnd"),    0.0,  -90.0,   500,   1  );
46 Param( HalfStepsStart,     double, wxT("PitchHalfStepsStart"),     0.0,  -12.0,   12.0,  1  );
47 Param( HalfStepsEnd,       double, wxT("PitchHalfStepsEnd"),       0.0,  -12.0,   12.0,  1  );
48 Param( PitchPercentStart,  double, wxT("PitchPercentChangeStart"), 0.0,  -50.0,   100.0, 1  );
49 Param( PitchPercentEnd,    double, wxT("PitchPercentChangeEnd"),   0.0,  -50.0,   100.0, 1  );
50 
51 //
52 // EffectTimeScale
53 //
54 
55 const ComponentInterfaceSymbol EffectTimeScale::Symbol
56 { wxT("Sliding Stretch"), XO("Sliding Stretch") };
57 
58 namespace{ BuiltinEffectsModule::Registration< EffectTimeScale > reg; }
59 
BEGIN_EVENT_TABLE(EffectTimeScale,wxEvtHandler)60 BEGIN_EVENT_TABLE(EffectTimeScale, wxEvtHandler)
61    EVT_TEXT(ID_RatePercentChangeStart, EffectTimeScale::OnText_RatePercentChangeStart)
62    EVT_TEXT(ID_RatePercentChangeEnd, EffectTimeScale::OnText_RatePercentChangeEnd)
63    EVT_TEXT(ID_PitchHalfStepsStart, EffectTimeScale::OnText_PitchHalfStepsStart)
64    EVT_TEXT(ID_PitchHalfStepsEnd, EffectTimeScale::OnText_PitchHalfStepsEnd)
65    EVT_TEXT(ID_PitchPercentChangeStart, EffectTimeScale::OnText_PitchPercentChangeStart)
66    EVT_TEXT(ID_PitchPercentChangeEnd, EffectTimeScale::OnText_PitchPercentChangeEnd)
67    EVT_SLIDER(ID_RatePercentChangeStart, EffectTimeScale::OnSlider_RatePercentChangeStart)
68    EVT_SLIDER(ID_RatePercentChangeEnd, EffectTimeScale::OnSlider_RatePercentChangeEnd)
69 END_EVENT_TABLE()
70 
71 EffectTimeScale::EffectTimeScale()
72 {
73    m_RatePercentChangeStart = DEF_RatePercentStart;
74    m_RatePercentChangeEnd = DEF_RatePercentEnd;
75    m_PitchHalfStepsStart = DEF_HalfStepsStart;
76    m_PitchHalfStepsEnd = DEF_HalfStepsEnd;
77    m_PitchPercentChangeStart = DEF_PitchPercentStart;
78    m_PitchPercentChangeEnd = DEF_PitchPercentEnd;
79 
80    slideTypeRate = SlideLinearOutputRate;
81    slideTypePitch = SlideLinearOutputRate;
82    bPreview = false;
83    previewSelectedDuration = 0.0;
84 
85    SetLinearEffectFlag(true);
86 }
87 
~EffectTimeScale()88 EffectTimeScale::~EffectTimeScale()
89 {
90 }
91 
92 // ComponentInterface implementation
93 
GetSymbol()94 ComponentInterfaceSymbol EffectTimeScale::GetSymbol()
95 {
96    return Symbol;
97 }
98 
GetDescription()99 TranslatableString EffectTimeScale::GetDescription()
100 {
101    return XO("Allows continuous changes to the tempo and/or pitch");
102 }
103 
ManualPage()104 ManualPageID EffectTimeScale::ManualPage()
105 {
106    return L"Sliding_Stretch";
107 }
108 
109 // EffectDefinitionInterface implementation
110 
GetType()111 EffectType EffectTimeScale::GetType()
112 {
113    return EffectTypeProcess;
114 }
115 
116 // EffectClientInterface implementation
DefineParams(ShuttleParams & S)117 bool EffectTimeScale::DefineParams( ShuttleParams & S ){
118    S.SHUTTLE_PARAM( m_RatePercentChangeStart,  RatePercentStart );
119    S.SHUTTLE_PARAM( m_RatePercentChangeEnd,    RatePercentEnd );
120    S.SHUTTLE_PARAM( m_PitchHalfStepsStart,     HalfStepsStart );
121    S.SHUTTLE_PARAM( m_PitchHalfStepsEnd,       HalfStepsEnd );
122    S.SHUTTLE_PARAM( m_PitchPercentChangeStart, PitchPercentStart );
123    S.SHUTTLE_PARAM( m_PitchPercentChangeEnd,   PitchPercentEnd );
124    return true;
125 }
126 
GetAutomationParameters(CommandParameters & parms)127 bool EffectTimeScale::GetAutomationParameters(CommandParameters & parms)
128 {
129    parms.Write(KEY_RatePercentStart, m_RatePercentChangeStart);
130    parms.Write(KEY_RatePercentEnd, m_RatePercentChangeEnd);
131    parms.Write(KEY_HalfStepsStart, m_PitchHalfStepsStart);
132    parms.Write(KEY_HalfStepsEnd, m_PitchHalfStepsEnd);
133    parms.Write(KEY_PitchPercentStart, m_PitchPercentChangeStart);
134    parms.Write(KEY_PitchPercentEnd, m_PitchPercentChangeEnd);
135 
136    return true;
137 }
138 
SetAutomationParameters(CommandParameters & parms)139 bool EffectTimeScale::SetAutomationParameters(CommandParameters & parms)
140 {
141    ReadAndVerifyDouble(RatePercentStart);
142    ReadAndVerifyDouble(RatePercentEnd);
143    ReadAndVerifyDouble(HalfStepsStart);
144    ReadAndVerifyDouble(HalfStepsEnd);
145    ReadAndVerifyDouble(PitchPercentStart);
146    ReadAndVerifyDouble(PitchPercentEnd);
147 
148    m_RatePercentChangeStart = RatePercentStart;
149    m_RatePercentChangeEnd = RatePercentEnd;
150    m_PitchHalfStepsStart = HalfStepsStart;
151    m_PitchHalfStepsEnd = HalfStepsEnd;
152    m_PitchPercentChangeStart = PitchPercentStart;
153    m_PitchPercentChangeEnd = PitchPercentEnd;
154 
155    return true;
156 }
157 
158 // Effect implementation
159 
Init()160 bool EffectTimeScale::Init()
161 {
162    return true;
163 }
164 
CalcPreviewInputLength(double previewLength)165 double EffectTimeScale::CalcPreviewInputLength(double previewLength)
166 {
167    double inputLength = Effect::GetDuration();
168    if(inputLength == 0.0) {
169       return 0.0;
170    } else {
171       double rateStart1 = PercentChangeToRatio(m_RatePercentChangeStart);
172       double rateEnd1 = PercentChangeToRatio(m_RatePercentChangeEnd);
173       double tOut = previewLength/inputLength;
174       double t = EffectSBSMS::getInvertedStretchedTime(rateStart1,rateEnd1,slideTypeRate,tOut);
175       return t * inputLength;
176    }
177 }
178 
Preview(bool dryOnly)179 void EffectTimeScale::Preview(bool dryOnly)
180 {
181    previewSelectedDuration = Effect::GetDuration();
182    auto cleanup = valueRestorer( bPreview, true );
183    Effect::Preview(dryOnly);
184 }
185 
Process()186 bool EffectTimeScale::Process()
187 {
188    double pitchStart1 = PercentChangeToRatio(m_PitchPercentChangeStart);
189    double pitchEnd1 = PercentChangeToRatio(m_PitchPercentChangeEnd);
190    double rateStart1 = PercentChangeToRatio(m_RatePercentChangeStart);
191    double rateEnd1 = PercentChangeToRatio(m_RatePercentChangeEnd);
192 
193    if(bPreview) {
194       double t = (mT1-mT0) / previewSelectedDuration;
195       rateEnd1 = EffectSBSMS::getRate(rateStart1,rateEnd1,slideTypeRate,t);
196       pitchEnd1 = EffectSBSMS::getRate(pitchStart1,pitchEnd1,slideTypePitch,t);
197    }
198 
199    EffectSBSMS::setParameters(rateStart1,rateEnd1,pitchStart1,pitchEnd1,slideTypeRate,slideTypePitch,false,false,false);
200    return EffectSBSMS::Process();
201 }
202 
PopulateOrExchange(ShuttleGui & S)203 void EffectTimeScale::PopulateOrExchange(ShuttleGui & S)
204 {
205    S.SetBorder(5);
206    S.AddSpace(0, 5);
207 
208    S.StartMultiColumn(2, wxALIGN_CENTER);
209    {
210       // Rate Start
211       S.StartStatic(XO("Initial Tempo Change (%)"));
212       {
213          S.StartMultiColumn(1, wxCENTER);
214          {
215             m_pTextCtrl_RatePercentChangeStart = S.Id(ID_RatePercentChangeStart)
216                .Validator<FloatingPointValidator<double>>(
217                   3, &m_RatePercentChangeStart,
218                   NumValidatorStyle::NO_TRAILING_ZEROES,
219                   MIN_RatePercentStart, MAX_RatePercentStart
220                )
221                .AddTextBox( {}, wxT(""), 12);
222          }
223          S.EndMultiColumn();
224          S.StartHorizontalLay(wxEXPAND, 0);
225          {
226             m_pSlider_RatePercentChangeStart = S.Id(ID_RatePercentChangeStart)
227                .Style(wxSL_HORIZONTAL)
228                .AddSlider( {}, DEF_RatePercentStart, MAX_RatePercentStart, MIN_RatePercentStart);
229          }
230          S.EndHorizontalLay();
231       }
232       S.EndStatic();
233 
234       S.StartStatic(XO("Final Tempo Change (%)"));
235       {
236          S.StartMultiColumn(1, wxCENTER);
237          {
238             m_pTextCtrl_RatePercentChangeEnd = S.Id(ID_RatePercentChangeEnd)
239                .Validator<FloatingPointValidator<double>>(
240                   3, &m_RatePercentChangeEnd,
241                   NumValidatorStyle::NO_TRAILING_ZEROES,
242                   MIN_RatePercentEnd, MAX_RatePercentEnd
243                )
244                .AddTextBox( {}, wxT(""), 12);
245          }
246          S.EndMultiColumn();
247          S.StartHorizontalLay(wxEXPAND, 0);
248          {
249             m_pSlider_RatePercentChangeEnd = S.Id(ID_RatePercentChangeEnd)
250                .Style(wxSL_HORIZONTAL)
251                .AddSlider( {}, DEF_RatePercentEnd, MAX_RatePercentEnd, MIN_RatePercentEnd);
252          }
253          S.EndHorizontalLay();
254       }
255       S.EndStatic();
256 
257       // Pitch Start
258       S.StartStatic(XO("Initial Pitch Shift"));
259       {
260          S.StartMultiColumn(2, wxCENTER);
261          {
262             m_pTextCtrl_PitchHalfStepsStart = S.Id(ID_PitchHalfStepsStart)
263                .Validator<FloatingPointValidator<double>>(
264                   3, &m_PitchHalfStepsStart,
265                   NumValidatorStyle::NO_TRAILING_ZEROES,
266                   MIN_HalfStepsStart, MAX_HalfStepsStart
267                )
268                .AddTextBox(XXO("(&semitones) [-12 to 12]:"), wxT(""), 12);
269 
270 
271             m_pTextCtrl_PitchPercentChangeStart = S.Id(ID_PitchPercentChangeStart)
272                .Validator<FloatingPointValidator<double>>(
273                   3, &m_PitchPercentChangeStart,
274                   NumValidatorStyle::NO_TRAILING_ZEROES,
275                   MIN_PitchPercentStart, MAX_PitchPercentStart
276                )
277                .AddTextBox(XXO("(%) [-50 to 100]:"), wxT(""), 12);
278          }
279          S.EndMultiColumn();
280       }
281       S.EndStatic();
282 
283       // Pitch End
284       S.StartStatic(XO("Final Pitch Shift"));
285       {
286          S.StartMultiColumn(2, wxCENTER);
287          {
288             m_pTextCtrl_PitchHalfStepsEnd = S.Id(ID_PitchHalfStepsEnd)
289                .Validator<FloatingPointValidator<double>>(
290                   3, &m_PitchHalfStepsEnd,
291                   NumValidatorStyle::NO_TRAILING_ZEROES,
292                   MIN_HalfStepsEnd, MAX_HalfStepsEnd
293                )
294                .AddTextBox(XXO("(s&emitones) [-12 to 12]:"), wxT(""), 12);
295 
296             m_pTextCtrl_PitchPercentChangeEnd = S.Id(ID_PitchPercentChangeEnd)
297                .Validator<FloatingPointValidator<double>>(
298                   3, &m_PitchPercentChangeEnd,
299                   NumValidatorStyle::NO_TRAILING_ZEROES,
300                   MIN_PitchPercentStart, MAX_PitchPercentStart)
301                .AddTextBox(XXO("(%) [-50 to 100]:"), wxT(""), 12);
302          }
303          S.EndMultiColumn();
304       }
305       S.EndStatic();
306    }
307    S.EndMultiColumn();
308 
309    return;
310 }
311 
TransferDataToWindow()312 bool EffectTimeScale::TransferDataToWindow()
313 {
314    if (!mUIParent->TransferDataToWindow())
315    {
316       return false;
317    }
318 
319    Update_Slider_RatePercentChangeStart();
320    Update_Slider_RatePercentChangeEnd();
321 
322    return true;
323 }
324 
TransferDataFromWindow()325 bool EffectTimeScale::TransferDataFromWindow()
326 {
327    if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
328    {
329       return false;
330    }
331 
332    return true;
333 }
334 
PercentChangeToRatio(double percentChange)335 inline double EffectTimeScale::PercentChangeToRatio(double percentChange)
336 {
337    return 1.0 + percentChange / 100.0;
338 }
339 
HalfStepsToPercentChange(double halfSteps)340 inline double EffectTimeScale::HalfStepsToPercentChange(double halfSteps)
341 {
342    return 100.0 * (pow(2.0,halfSteps/12.0) - 1.0);
343 }
344 
PercentChangeToHalfSteps(double percentChange)345 inline double EffectTimeScale::PercentChangeToHalfSteps(double percentChange)
346 {
347    return 12.0 * log2(PercentChangeToRatio(percentChange));
348 }
349 
Update_Text_RatePercentChangeStart()350 void EffectTimeScale::Update_Text_RatePercentChangeStart()
351 {
352    m_pTextCtrl_RatePercentChangeStart->GetValidator()->TransferToWindow();
353 }
354 
Update_Text_RatePercentChangeEnd()355 void EffectTimeScale::Update_Text_RatePercentChangeEnd()
356 {
357    m_pTextCtrl_RatePercentChangeEnd->GetValidator()->TransferToWindow();
358 }
359 
Update_Slider_RatePercentChangeStart()360 void EffectTimeScale::Update_Slider_RatePercentChangeStart()
361 {
362    m_pSlider_RatePercentChangeStart->SetValue((int)(m_RatePercentChangeStart + 0.5));
363 }
364 
Update_Slider_RatePercentChangeEnd()365 void EffectTimeScale::Update_Slider_RatePercentChangeEnd()
366 {
367    m_pSlider_RatePercentChangeEnd->SetValue((int)(m_RatePercentChangeEnd + 0.5));
368 }
369 
Update_Text_PitchHalfStepsStart()370 void EffectTimeScale::Update_Text_PitchHalfStepsStart()
371 {
372    m_pTextCtrl_PitchHalfStepsStart->GetValidator()->TransferToWindow();
373 }
374 
Update_Text_PitchHalfStepsEnd()375 void EffectTimeScale::Update_Text_PitchHalfStepsEnd()
376 {
377    m_pTextCtrl_PitchHalfStepsEnd->GetValidator()->TransferToWindow();
378 }
379 
Update_Text_PitchPercentChangeStart()380 void EffectTimeScale::Update_Text_PitchPercentChangeStart()
381 {
382    m_pTextCtrl_PitchPercentChangeStart->GetValidator()->TransferToWindow();
383 }
384 
Update_Text_PitchPercentChangeEnd()385 void EffectTimeScale::Update_Text_PitchPercentChangeEnd()
386 {
387    m_pTextCtrl_PitchPercentChangeEnd->GetValidator()->TransferToWindow();
388 }
389 
OnText_RatePercentChangeStart(wxCommandEvent & WXUNUSED (evt))390 void EffectTimeScale::OnText_RatePercentChangeStart(wxCommandEvent & WXUNUSED(evt))
391 {
392    if (!EnableApply(mUIParent->TransferDataFromWindow()))
393    {
394       return;
395    }
396 
397    Update_Slider_RatePercentChangeStart();
398 }
399 
OnText_RatePercentChangeEnd(wxCommandEvent & WXUNUSED (evt))400 void EffectTimeScale::OnText_RatePercentChangeEnd(wxCommandEvent & WXUNUSED(evt))
401 {
402    if (!EnableApply(mUIParent->TransferDataFromWindow()))
403    {
404       return;
405    }
406 
407    Update_Slider_RatePercentChangeEnd();
408 }
409 
OnSlider_RatePercentChangeStart(wxCommandEvent & evt)410 void EffectTimeScale::OnSlider_RatePercentChangeStart(wxCommandEvent & evt)
411 {
412    m_RatePercentChangeStart = (double) evt.GetInt();
413 
414    Update_Text_RatePercentChangeStart();
415 }
416 
OnSlider_RatePercentChangeEnd(wxCommandEvent & evt)417 void EffectTimeScale::OnSlider_RatePercentChangeEnd(wxCommandEvent & evt)
418 {
419    m_RatePercentChangeEnd = (double) evt.GetInt();
420 
421    Update_Text_RatePercentChangeEnd();
422 }
423 
OnText_PitchHalfStepsStart(wxCommandEvent & WXUNUSED (evt))424 void EffectTimeScale::OnText_PitchHalfStepsStart(wxCommandEvent & WXUNUSED(evt))
425 {
426    if (!EnableApply(mUIParent->TransferDataFromWindow()))
427    {
428       return;
429    }
430 
431    m_PitchPercentChangeStart = HalfStepsToPercentChange(m_PitchHalfStepsStart);
432    Update_Text_PitchPercentChangeStart();
433 }
434 
OnText_PitchHalfStepsEnd(wxCommandEvent & WXUNUSED (evt))435 void EffectTimeScale::OnText_PitchHalfStepsEnd(wxCommandEvent & WXUNUSED(evt))
436 {
437    if (!EnableApply(mUIParent->TransferDataFromWindow()))
438    {
439       return;
440    }
441 
442    m_PitchPercentChangeEnd = HalfStepsToPercentChange(m_PitchHalfStepsEnd);
443    Update_Text_PitchPercentChangeEnd();
444 }
445 
OnText_PitchPercentChangeStart(wxCommandEvent & WXUNUSED (evt))446 void EffectTimeScale::OnText_PitchPercentChangeStart(wxCommandEvent & WXUNUSED(evt))
447 {
448    if (!EnableApply(mUIParent->TransferDataFromWindow()))
449    {
450       return;
451    }
452 
453    m_PitchHalfStepsStart = PercentChangeToHalfSteps(m_PitchPercentChangeStart);
454    Update_Text_PitchHalfStepsStart();
455 }
456 
OnText_PitchPercentChangeEnd(wxCommandEvent & WXUNUSED (evt))457 void EffectTimeScale::OnText_PitchPercentChangeEnd(wxCommandEvent & WXUNUSED(evt))
458 {
459    if (!EnableApply(mUIParent->TransferDataFromWindow()))
460    {
461       return;
462    }
463 
464    m_PitchHalfStepsEnd = PercentChangeToHalfSteps(m_PitchPercentChangeEnd);
465    Update_Text_PitchHalfStepsEnd();
466 }
467 
468 #endif
469