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