Audacity 3.2.0
ChangeSpeed.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ChangeSpeed.cpp
6
7 Vaughan Johnson, Dominic Mazzoni
8
9*******************************************************************//*******************************************************************/
15
16
17#include "ChangeSpeed.h"
18#include "ConfigInterface.h"
19#include "EffectEditor.h"
20#include "LoadEffects.h"
21
22#include <wx/choice.h>
23#include <wx/slider.h>
24
25#include "ShuttleGui.h"
26#include "../widgets/NumericTextCtrl.h"
27#include "../widgets/valnum.h"
28
30
31enum
32{
38};
39
41 XO("33\u2153"),
42 XO("45"),
43 XO("78"),
44 /* i18n-hint: n/a is an English abbreviation meaning "not applicable". */
45 XO("n/a"),
46};
47
48// We warp the slider to go up to 400%, but user can enter higher values
49static const double kSliderMax = 100.0; // warped above zero to actually go up to 400%
50static const double kSliderWarp = 1.30105; // warp power takes max from 100 to 400.
51
53
54BEGIN_EVENT_TABLE(EffectChangeSpeed, wxEvtHandler)
63
64std::unique_ptr<EffectEditor> EffectChangeSpeed::PopulateOrExchange(
66 const EffectOutputs *)
67{
68 mUIParent = S.GetParent();
69
70 {
71 wxString formatId;
72 GetConfig(GetDefinition(), PluginSettings::Private,
74 wxT("TimeFormat"), formatId, mFormat.GET());
78 }
79 GetConfig(GetDefinition(), PluginSettings::Private,
81 wxT("VinylChoice"), mFromVinyl, mFromVinyl);
82
83 S.SetBorder(5);
84
85 S.StartVerticalLay(0);
86 {
87 // Speed multiplier and percent change controls.
88 S.StartMultiColumn(4, wxCENTER);
89 {
90 mpTextCtrl_Multiplier = S.Id(ID_Multiplier)
91 .Validator<FloatingPointValidator<double>>(
92 3, &mMultiplier,
93 NumValidatorStyle::THREE_TRAILING_ZEROES,
94 Percentage.min / 100.0, ((Percentage.max / 100.0) + 1) )
95 .AddTextBox(XXO("&Speed Multiplier:"), L"", 12);
96
97 mpTextCtrl_PercentChange = S.Id(ID_PercentChange)
98 .Validator<FloatingPointValidator<double>>(
99 3, &m_PercentChange,
100 NumValidatorStyle::THREE_TRAILING_ZEROES,
101 Percentage.min, Percentage.max )
102 .AddTextBox(XXO("Percent C&hange:"), L"", 12);
103 }
104 S.EndMultiColumn();
105
106 // Percent change slider.
107 S.StartHorizontalLay(wxEXPAND);
108 {
109 mpSlider_PercentChange = S.Id(ID_PercentChange)
110 .Name(XO("Percent Change"))
111 .Style(wxSL_HORIZONTAL)
112 .AddSlider( {}, 0, (int)kSliderMax, (int)Percentage.min);
113 }
114 S.EndHorizontalLay();
115
116 // Vinyl rpm controls.
117 S.StartMultiColumn(5, wxCENTER);
118 {
119 /* i18n-hint: "rpm" is an English abbreviation meaning "revolutions per minute".
120 "vinyl" refers to old-fashioned phonograph records */
121 S.AddUnits(XO("Standard Vinyl rpm:"));
122
123 mpChoice_FromVinyl = S.Id(ID_FromVinyl)
124 /* i18n-hint: changing speed of audio "from" one value "to" another
125 "rpm" means "revolutions per minute" as on a vinyl record turntable
126 */
127 .Name(XO("From rpm"))
128 .MinSize( { 100, -1 } )
129 /* i18n-hint: changing speed of audio "from" one value "to" another */
130 .AddChoice(XXC("&from", "change speed"), kVinylStrings);
131
132 mpChoice_ToVinyl = S.Id(ID_ToVinyl)
133 /* i18n-hint: changing speed of audio "from" one value "to" another
134 "rpm" means "revolutions per minute" as on a vinyl record turntable
135 */
136 .Name(XO("To rpm"))
137 .MinSize( { 100, -1 } )
138 /* i18n-hint: changing speed of audio "from" one value "to" another */
139 .AddChoice(XXC("&to", "change speed"), kVinylStrings);
140 }
141 S.EndMultiColumn();
142
143 // From/To time controls.
144 S.StartStatic(XO("Selection Length"), 0);
145 {
146 S.StartMultiColumn(2, wxALIGN_LEFT);
147 {
148 S.AddPrompt(XXO("C&urrent Length:"));
149
150 mpFromLengthCtrl = safenew
152 S.GetParent(), wxID_ANY,
154 mFormat,
155 mFromLength,
157 .ReadOnly(true)
158 .MenuEnabled(false));
159
160 S.ToolTip(XO("Current length of selection."))
161 /* i18n-hint: changing speed of audio "from" one value "to" another */
162 .Name(XC("from", "change speed"))
163 .Position(wxALIGN_LEFT)
164 .AddWindow(mpFromLengthCtrl);
165
166 S.AddPrompt(XXO("&New Length:"));
167
168 mpToLengthCtrl = safenew
170 S.GetParent(), ID_ToLength,
172 mFormat,
173 mToLength);
174
175 /* i18n-hint: changing speed of audio "from" one value "to" another */
176 S.Name(XC("to", "change speed"))
177 .Position(wxALIGN_LEFT)
178 .AddWindow(mpToLengthCtrl);
179 }
180 S.EndMultiColumn();
181 }
182 S.EndStatic();
183 }
184 S.EndVerticalLay();
185 return nullptr;
186}
187
189{
190 mbLoopDetect = true;
191
192 if (!mUIParent->TransferDataToWindow())
193 {
194 return false;
195 }
196
197 if (mFromVinyl == kVinyl_NA)
198 {
200 }
201
206
207 // Set from/to Vinyl controls - mFromVinyl must be set first.
208 mpChoice_FromVinyl->SetSelection(mFromVinyl);
209 // Then update to get correct mToVinyl.
210 Update_Vinyl();
211 // Then update ToVinyl control.
212 mpChoice_ToVinyl->SetSelection(mToVinyl);
213
214 // Set From Length control.
215 // Set the format first so we can get sample accuracy.
218
219 mbLoopDetect = false;
220
221 return true;
222}
223
225{
226 // mUIParent->TransferDataFromWindow() loses some precision, so save and restore it.
227 double exactPercent = m_PercentChange;
228 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
229 {
230 return false;
231 }
232 m_PercentChange = exactPercent;
233
234 // TODO: just visit these effect settings the default way
236 CurrentSettingsGroup(), wxT("TimeFormat"), mFormat.GET());
238 CurrentSettingsGroup(), wxT("VinylChoice"), mFromVinyl);
239
240 return true;
241}
242
243// handler implementations for ChangeSpeedBase
244
245void EffectChangeSpeed::OnText_PercentChange(wxCommandEvent & WXUNUSED(evt))
246{
247 if (mbLoopDetect)
248 return;
249
250 mpTextCtrl_PercentChange->GetValidator()->TransferFromWindow();
251 UpdateUI();
252
253 mbLoopDetect = true;
256 Update_Vinyl();
258 mbLoopDetect = false;
259}
260
261void EffectChangeSpeed::OnText_Multiplier(wxCommandEvent & WXUNUSED(evt))
262{
263 if (mbLoopDetect)
264 return;
265
266 mpTextCtrl_Multiplier->GetValidator()->TransferFromWindow();
267 m_PercentChange = 100 * (mMultiplier - 1);
268 UpdateUI();
269
270 mbLoopDetect = true;
273 Update_Vinyl();
275 mbLoopDetect = false;
276}
277
278void EffectChangeSpeed::OnSlider_PercentChange(wxCommandEvent & WXUNUSED(evt))
279{
280 if (mbLoopDetect)
281 return;
282
283 m_PercentChange = (double)(mpSlider_PercentChange->GetValue());
284 // Warp positive values to actually go up faster & further than negatives.
285 if (m_PercentChange > 0.0)
287 UpdateUI();
288
289 mbLoopDetect = true;
292 Update_Vinyl();
294 mbLoopDetect = false;
295}
296
297void EffectChangeSpeed::OnChoice_Vinyl(wxCommandEvent & WXUNUSED(evt))
298{
299 // Treat mpChoice_FromVinyl and mpChoice_ToVinyl as one control since we need
300 // both to calculate Percent Change.
301 mFromVinyl = mpChoice_FromVinyl->GetSelection();
302 mToVinyl = mpChoice_ToVinyl->GetSelection();
303 // Use this as the 'preferred' choice.
304 if (mFromVinyl != kVinyl_NA) {
306 CurrentSettingsGroup(), wxT("VinylChoice"), mFromVinyl);
307 }
308
309 // If mFromVinyl & mToVinyl are set, then there's a NEW percent change.
310 if ((mFromVinyl != kVinyl_NA) && (mToVinyl != kVinyl_NA))
311 {
312 double fromRPM;
313 double toRPM;
314 switch (mFromVinyl) {
315 default:
316 case kVinyl_33AndAThird: fromRPM = 33.0 + (1.0 / 3.0); break;
317 case kVinyl_45: fromRPM = 45.0; break;
318 case kVinyl_78: fromRPM = 78; break;
319 }
320 switch (mToVinyl) {
321 default:
322 case kVinyl_33AndAThird: toRPM = 33.0 + (1.0 / 3.0); break;
323 case kVinyl_45: toRPM = 45.0; break;
324 case kVinyl_78: toRPM = 78; break;
325 }
326 m_PercentChange = ((toRPM * 100.0) / fromRPM) - 100.0;
327 UpdateUI();
328
329 mbLoopDetect = true;
334 }
335 mbLoopDetect = false;
336}
337
338void EffectChangeSpeed::OnTimeCtrl_ToLength(wxCommandEvent & WXUNUSED(evt))
339{
340 if (mbLoopDetect)
341 return;
342
344 // Division by (double) 0.0 is not an error and we want to show "infinite" in
345 // text controls, so take care that we handle infinite values when they occur.
346 m_PercentChange = ((mFromLength * 100.0) / mToLength) - 100.0;
347 UpdateUI();
348
349 mbLoopDetect = true;
350
354 Update_Vinyl();
355
356 mbLoopDetect = false;
357}
358
359void EffectChangeSpeed::OnTimeCtrlUpdate(wxCommandEvent & evt)
360{
363 NumericConverterType_TIME(), evt.GetString()).Internal();
364
366 // Update From/To Length controls (precision has changed).
369}
370
371// helper functions
372
374// Update Text Percent control from percent change.
375{
376 mpTextCtrl_PercentChange->GetValidator()->TransferToWindow();
377}
378
380// Update Multiplier control from percent change.
381{
382 mMultiplier = 1 + (m_PercentChange) / 100.0;
383 mpTextCtrl_Multiplier->GetValidator()->TransferToWindow();
384}
385
387// Update Slider Percent control from percent change.
388{
389 auto unwarped = std::min<double>(m_PercentChange, Percentage.max);
390 if (unwarped > 0.0)
391 // Un-warp values above zero to actually go up to kSliderMax.
392 unwarped = pow(m_PercentChange, (1.0 / kSliderWarp));
393
394 // Caution: m_PercentChange could be infinite.
395 int unwarpedi = (int)(unwarped + 0.5);
396 unwarpedi = std::min<int>(unwarpedi, (int)kSliderMax);
397
398 mpSlider_PercentChange->SetValue(unwarpedi);
399}
400
402// Update Vinyl controls from percent change.
403{
404 // Match Vinyl rpm when within 0.01% of a standard ratio.
405 // Ratios calculated as: ((toRPM / fromRPM) - 1) * 100 * 100
406
407 // Caution: m_PercentChange could be infinite
408 int ratio = (int)((m_PercentChange * 100) + 0.5);
409
410 switch (ratio)
411 {
412 case 0: // toRPM is the same as fromRPM
413 if (mFromVinyl != kVinyl_NA) {
414 mpChoice_ToVinyl->SetSelection(mpChoice_FromVinyl->GetSelection());
415 } else {
416 // Use the last saved option.
418 CurrentSettingsGroup(), wxT("VinylChoice"), mFromVinyl, 0);
419 mpChoice_FromVinyl->SetSelection(mFromVinyl);
420 mpChoice_ToVinyl->SetSelection(mFromVinyl);
421 }
422 break;
423 case 3500:
425 mpChoice_ToVinyl->SetSelection(kVinyl_45);
426 break;
427 case 13400:
429 mpChoice_ToVinyl->SetSelection(kVinyl_78);
430 break;
431 case -2593:
432 mpChoice_FromVinyl->SetSelection(kVinyl_45);
434 break;
435 case 7333:
436 mpChoice_FromVinyl->SetSelection(kVinyl_45);
437 mpChoice_ToVinyl->SetSelection(kVinyl_78);
438 break;
439 case -5727:
440 mpChoice_FromVinyl->SetSelection(kVinyl_78);
442 break;
443 case -4231:
444 mpChoice_FromVinyl->SetSelection(kVinyl_78);
445 mpChoice_ToVinyl->SetSelection(kVinyl_45);
446 break;
447 default:
448 mpChoice_ToVinyl->SetSelection(kVinyl_NA);
449 }
450 // and update variables.
451 mFromVinyl = mpChoice_FromVinyl->GetSelection();
452 mToVinyl = mpChoice_ToVinyl->GetSelection();
453}
454
456// Update ToLength control from percent change.
457{
458 mToLength = (mFromLength * 100.0) / (100.0 + m_PercentChange);
459
460 // Set the format first so we can get sample accuracy.
462 // Negative times do not make sense.
463 // 359999 = 99h:59m:59s which is a little less disturbing than overflow characters
464 // though it may still look a bit strange with some formats.
465 mToLength = std::clamp<double>(mToLength, 0.0, 359999.0);
467}
468
470// Disable OK and Preview if not in sensible range.
471{
474}
wxT("CloseDown"))
END_EVENT_TABLE()
@ ID_ToLength
Definition: ChangeSpeed.cpp:37
@ ID_FromVinyl
Definition: ChangeSpeed.cpp:35
@ ID_ToVinyl
Definition: ChangeSpeed.cpp:36
@ ID_Multiplier
Definition: ChangeSpeed.cpp:34
@ ID_PercentChange
Definition: ChangeSpeed.cpp:33
static const double kSliderMax
Definition: ChangeSpeed.cpp:49
static const TranslatableStrings kVinylStrings
Definition: ChangeSpeed.cpp:40
static const double kSliderWarp
Definition: ChangeSpeed.cpp:50
@ kVinyl_78
@ kVinyl_45
@ kVinyl_33AndAThird
@ kVinyl_NA
const RegistryPath & CurrentSettingsGroup()
Component of a configuration key path, for last-used destructive settings.
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define XXC(s, c)
Definition: Internat.h:47
#define XC(s, c)
Definition: Internat.h:37
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:89
#define safenew
Definition: MemoryX.h:10
const NumericConverterType & NumericConverterType_TIME()
#define S(N)
Definition: ToChars.cpp:64
std::vector< TranslatableString > TranslatableStrings
NumericFormatID mFormat
static constexpr EffectParameter Percentage
const wxString & Internal() const
double mProjectRate
Definition: EffectBase.h:119
void OnText_PercentChange(wxCommandEvent &evt)
NumericTextCtrl * mpToLengthCtrl
Definition: ChangeSpeed.h:68
void OnChoice_Vinyl(wxCommandEvent &evt)
void OnText_Multiplier(wxCommandEvent &evt)
bool TransferDataToWindow(const EffectSettings &settings) override
void Update_Slider_PercentChange()
wxTextCtrl * mpTextCtrl_Multiplier
Definition: ChangeSpeed.h:63
void Update_TimeCtrl_ToLength()
wxChoice * mpChoice_ToVinyl
Definition: ChangeSpeed.h:66
wxTextCtrl * mpTextCtrl_PercentChange
Definition: ChangeSpeed.h:62
wxWeakRef< wxWindow > mUIParent
Definition: ChangeSpeed.h:59
void Update_Text_PercentChange()
wxChoice * mpChoice_FromVinyl
Definition: ChangeSpeed.h:65
void OnTimeCtrl_ToLength(wxCommandEvent &evt)
void Update_Text_Multiplier()
void OnSlider_PercentChange(wxCommandEvent &evt)
bool TransferDataFromWindow(EffectSettings &settings) override
void OnTimeCtrlUpdate(wxCommandEvent &evt)
wxSlider * mpSlider_PercentChange
Definition: ChangeSpeed.h:64
NumericTextCtrl * mpFromLengthCtrl
Definition: ChangeSpeed.h:67
static bool EnableApply(wxWindow *parent, bool enable=true)
Enable or disable the Apply button of the dialog that contains parent.
const EffectSettingsManager & GetDefinition() const override
Definition: Effect.cpp:182
Performs effect computation.
Hold values to send to effect output meters.
static FormatterContext SampleRateContext(double sampleRate)
const wxString & GET() const
Explicit conversion to wxString, meant to be ugly-looking and demanding of a comment why it's correct...
Definition: Identifier.h:66
bool SetFormatName(const NumericFormatID &formatName)
void SetValue(double newValue)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
NUMERIC_FORMATS_API NumericFormatSymbol Lookup(const FormatterContext &context, const NumericConverterType &type, const NumericFormatID &formatIdentifier)
Looks up the format, returns Default for the type if the format is not registered.
bool SetConfig(const EffectDefinitionInterface &ident, ConfigurationType type, const RegistryPath &group, const RegistryPath &key, const Value &value)
bool GetConfig(const EffectDefinitionInterface &ident, ConfigurationType type, const RegistryPath &group, const RegistryPath &key, Value &var, const Value &defval)
BuiltinEffectsModule::Registration< EffectChangeSpeed > reg
Definition: ChangeSpeed.cpp:52
STL namespace.
const Type min
Minimum value.
const Type max
Maximum value.
Externalized state of a plug-in.
Options & ReadOnly(bool enable)