Audacity 3.2.0
VST3ParametersWindow.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file VST3ParametersWindow.cpp
6
7 @author Vitaly Sverchinsky
8
9 @brief Part of Audacity VST3 module
10
11**********************************************************************/
12
13
15
16#include <pluginterfaces/vst/ivsteditcontroller.h>
17
18#include <wx/sizer.h>
19#include <wx/stattext.h>
20#include <wx/checkbox.h>
21#include <wx/choice.h>
22#include <wx/slider.h>
23
24#include "MemoryX.h"
25#include "VST3Utils.h"
26#if wxUSE_ACCESSIBILITY
27#include "../../widgets/WindowAccessible.h"
28#endif
29
30
31//Interface between wx control and IEditController parameter.
33{
34 const Steinberg::Vst::ParamID mParameterId;
35public:
36 VST3ParameterControl(Steinberg::Vst::ParamID id) : mParameterId(id) { }
37
39 //Read parameter value from the controller and update UI
40 virtual void SetNormalizedValue(Steinberg::Vst::IEditController&, Steinberg::Vst::ParamValue value) = 0;
41 //Convert current control value to normalized parameter value
42 virtual Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController& editController) const = 0;
43 //Update a control's accessible object if necessary
44 virtual void UpdateAccessible(Steinberg::Vst::IEditController& editController, Steinberg::Vst::ParamValue value) {}
45
46 Steinberg::Vst::ParamID GetParameterId() const noexcept { return mParameterId; }
47};
48
49namespace
50{
51 class VST3ValueText final : public wxStaticText, public VST3ParameterControl
52 {
53 const wxString mUnits;
54 public:
55 VST3ValueText(wxWindow *parent,
56 wxWindowID id,
57 Steinberg::Vst::ParamID paramId,
58 const wxString& units,
59 const wxPoint& pos = wxDefaultPosition,
60 const wxSize& size = wxDefaultSize,
61 long style = 0,
62 const wxString& name = wxStaticTextNameStr)
63 : wxStaticText(parent, id, wxEmptyString, pos, size, style, name)
64 , VST3ParameterControl(paramId)
65 , mUnits(units) { }
66
67 void SetNormalizedValue(Steinberg::Vst::IEditController& editController, Steinberg::Vst::ParamValue value) override
68 {
69 Steinberg::Vst::String128 str { };
70 editController.getParamStringByValue(GetParameterId(), value, str);
71 if(mUnits.empty())
72 SetLabel(VST3Utils::ToWxString(str));
73 else
74 SetLabel(wxString::Format("%s %s", VST3Utils::ToWxString(str), mUnits));
75 }
76
77 Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController& editController) const override
78 {
79 return editController.getParamNormalized(GetParameterId());
80 }
81 };
82
83 class VST3ListParameter final : public wxChoice, public VST3ParameterControl
84 {
85 public:
86 VST3ListParameter(wxWindow *parent,
87 wxWindowID id,
88 Steinberg::Vst::ParamID paramId,
89 const wxPoint& pos = wxDefaultPosition,
90 const wxSize& size = wxDefaultSize,
91 long style = 0,
92 const wxValidator& validator = wxDefaultValidator,
93 const wxString& name = wxChoiceNameStr)
94 : wxChoice(parent, id, pos, size, 0, nullptr, style, validator, name)
95 , VST3ParameterControl(paramId) { }
96
97 void SetNormalizedValue(Steinberg::Vst::IEditController& editController, Steinberg::Vst::ParamValue value) override
98 {
99 SetSelection(static_cast<int>(editController.normalizedParamToPlain(GetParameterId(), value)));
100 }
101
102 Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController& editController) const override
103 {
104 return editController.plainParamToNormalized(GetParameterId(), GetSelection());
105 }
106 };
107
108 class VST3ContinuousParameter final : public wxSlider, public VST3ParameterControl
109 {
110 const wxString mTitle;
111 const wxString mUnits;
112
113 public:
114 static constexpr auto Step = 0.01;
115
116 VST3ContinuousParameter(wxWindow *parent,
117 wxWindowID id,
118 Steinberg::Vst::ParamID paramId,
119 const wxString& title,
120 const wxString& units,
121 const wxPoint& pos = wxDefaultPosition,
122 const wxSize& size = wxDefaultSize,
123 long style = wxSL_HORIZONTAL,
124 const wxValidator& validator = wxDefaultValidator,
125 const wxString& name = wxSliderNameStr)
126 : wxSlider(parent, id, 0, 0, static_cast<int>(1.0 / Step), pos, size, style, validator, name)
127 , VST3ParameterControl(paramId)
128 , mTitle(title)
129 , mUnits(units)
130 {
131#if wxUSE_ACCESSIBILITY
132 SetAccessible(safenew WindowAccessible(this));
133#endif
134 }
135
136 void SetNormalizedValue(Steinberg::Vst::IEditController& editController, Steinberg::Vst::ParamValue value) override
137 {
138 SetValue(static_cast<int>(value / Step));
139 UpdateAccessible(editController, value);
140 }
141
142 void UpdateAccessible(Steinberg::Vst::IEditController& editController, Steinberg::Vst::ParamValue value) override
143 {
144 Steinberg::Vst::String128 str{};
145 editController.getParamStringByValue(GetParameterId(), value, str);
146 SetName(wxString::Format("%s %s %s",
147 mTitle, VST3Utils::ToWxString(str), mUnits));
148 }
149
150 Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController&) const override
151 {
152 return GetValue() * Step;
153 }
154 };
155
156 class VST3DiscreteParameter final : public wxSlider, public VST3ParameterControl
157 {
158 const wxString mTitle;
159 const wxString mUnits;
160
161 public:
162 VST3DiscreteParameter(wxWindow *parent,
163 wxWindowID id,
164 Steinberg::Vst::ParamID paramId,
165 int maxValue,
166 const wxString& title,
167 const wxString& units,
168 const wxPoint& pos = wxDefaultPosition,
169 const wxSize& size = wxDefaultSize,
170 long style = wxSL_HORIZONTAL,
171 const wxValidator& validator = wxDefaultValidator,
172 const wxString& name = wxSliderNameStr)
173 : wxSlider(parent, id, 0, 0, maxValue, pos, size, style, validator, name)
174 , VST3ParameterControl(paramId)
175 , mTitle(title)
176 , mUnits(units)
177 {
178#if wxUSE_ACCESSIBILITY
179 SetAccessible(safenew WindowAccessible(this));
180#endif
181 }
182
183 void SetNormalizedValue(Steinberg::Vst::IEditController& editController, Steinberg::Vst::ParamValue value) override
184 {
185 SetValue(static_cast<int>(editController.normalizedParamToPlain(GetParameterId(), value)));
186 UpdateAccessible(editController, value);
187 }
188
189 void UpdateAccessible(Steinberg::Vst::IEditController& editController, Steinberg::Vst::ParamValue value) override
190 {
191 Steinberg::Vst::String128 str{ };
192 editController.getParamStringByValue(GetParameterId(), value, str);
193 SetName(wxString::Format("%s %s %s", mTitle, VST3Utils::ToWxString(str), mUnits));
194 }
195
196 Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController& editController) const override
197 {
198 return editController.plainParamToNormalized(GetParameterId(), GetValue());
199 }
200 };
201
202 class VST3ToggleParameter final : public wxCheckBox, public VST3ParameterControl
203 {
204 public:
205
206 VST3ToggleParameter(wxWindow *parent,
207 wxWindowID id,
208 const wxString& label,
209 Steinberg::Vst::ParamID paramId,
210 const wxPoint& pos = wxDefaultPosition,
211 const wxSize& size = wxDefaultSize,
212 long style = wxALIGN_RIGHT,
213 const wxValidator& validator = wxDefaultValidator,
214 const wxString& name = wxCheckBoxNameStr)
215 : wxCheckBox(parent, id, label, pos, size, style, validator, name)
216 , VST3ParameterControl(paramId) { }
217
218 void SetNormalizedValue(Steinberg::Vst::IEditController& editController, Steinberg::Vst::ParamValue value) override
219 {
220 SetValue(value != .0);
221 }
222
223 Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController&) const override
224 {
225 return GetValue() ? 1. : .0;
226 }
227 };
228}
229
231 Steinberg::Vst::IEditController& editController,
232 Steinberg::Vst::IComponentHandler& handler,
233 wxWindowID id,
234 const wxPoint& pos,
235 const wxSize& size,
236 long style,
237 const wxString& name)
238 : wxScrolledWindow(parent, id, pos, size, style, name)
239 , mEditController(&editController)
240 , mComponentHandler(&handler)
241{
242 using namespace Steinberg;
243
244 auto sizer = std::make_unique<wxGridSizer>(3, 5, 5);
245
246 for(int i = 0, count = editController.getParameterCount(); i < count; ++i)
247 {
248 Vst::ParameterInfo parameterInfo { };
249 if(editController.getParameterInfo(i, parameterInfo) != kResultOk)
250 continue;
251
252 if(parameterInfo.flags & (Vst::ParameterInfo::kIsHidden | Vst::ParameterInfo::kIsProgramChange))
253 continue;
254
255 if((parameterInfo.flags & (Vst::ParameterInfo::kCanAutomate | Vst::ParameterInfo::kIsBypass | Vst::ParameterInfo::kIsReadOnly)) == 0)
256 continue;
257
258 {
259 // Hide proxy parameters with name that starts with "MIDI CC "
260 // That prevents Plain UI from creating many useless controls
261 static_assert(sizeof(Steinberg::tchar) == sizeof(char16_t));
262 static const std::basic_string_view<tchar> MIDI_CC { reinterpret_cast<const tchar*>(u"MIDI CC ") };
263 if(std::basic_string_view<tchar>(parameterInfo.title).rfind(MIDI_CC, 0) == 0)
264 continue;
265 }
266
267 if (parameterInfo.stepCount != 1) // not a toggle
268 sizer->Add(safenew wxStaticText(
269 this,
270 wxID_ANY,
271 VST3Utils::ToWxString(parameterInfo.title),
272 wxDefaultPosition,
273 wxDefaultSize,
274 wxALIGN_RIGHT), 0, wxEXPAND
275 );
276
277 if(parameterInfo.flags & Vst::ParameterInfo::kIsReadOnly)
278 {
279 auto text = safenew VST3ValueText(
280 this,
281 wxID_ANY,
282 parameterInfo.id,
283 VST3Utils::ToWxString(parameterInfo.units)
284 );
285 sizer->Add(text);
286 sizer->AddStretchSpacer();
288 }
289 //toggle
290 else if(parameterInfo.stepCount == 1)
291 {
292 const auto toggle = safenew VST3ToggleParameter (this, wxID_ANY,
293 VST3Utils::ToWxString(parameterInfo.title), parameterInfo.id);
294 toggle->Bind(wxEVT_CHECKBOX, &VST3ParametersWindow::OnParameterValueChanged, this);
295 sizer->Add(toggle, 0, wxEXPAND);
296 sizer->AddStretchSpacer();
297 sizer->AddStretchSpacer();
299 }
300 //list
301 else if(parameterInfo.stepCount != 0 && (parameterInfo.flags & Vst::ParameterInfo::kIsList))
302 {
303 const auto list = safenew VST3ListParameter(this, wxID_ANY, parameterInfo.id);
304
305 for(auto j = 0; j <= parameterInfo.stepCount; ++j)
306 {
307 Vst::String128 displayValue = { 0 };
308 editController.getParamStringByValue(
309 parameterInfo.id,
310 editController.plainParamToNormalized(parameterInfo.id, j),
311 displayValue
312 );
313 list->AppendString(VST3Utils::ToWxString(displayValue));
314 }
315 list->Bind(wxEVT_CHOICE, &VST3ParametersWindow::OnParameterValueChanged, this);
316 sizer->Add(list, 0, wxEXPAND);
317 sizer->AddStretchSpacer();
319 }
320 else
321 {
322 //continuous
323 if(parameterInfo.stepCount == 0)
324 {
325 auto slider = safenew VST3ContinuousParameter(this, wxID_ANY, parameterInfo.id,
326 VST3Utils::ToWxString(parameterInfo.title), VST3Utils::ToWxString(parameterInfo.units));
327 sizer->Add(slider, 0, wxEXPAND);
328 slider->Bind(wxEVT_SLIDER, &VST3ParametersWindow::OnParameterValueChanged, this);
330 }
331 //discrete
332 else
333 {
334 auto slider = safenew VST3DiscreteParameter(this, wxID_ANY, parameterInfo.id, parameterInfo.stepCount,
335 VST3Utils::ToWxString(parameterInfo.title), VST3Utils::ToWxString(parameterInfo.units));
336 sizer->Add(slider, 0, wxEXPAND);
337 slider->Bind(wxEVT_SLIDER, &VST3ParametersWindow::OnParameterValueChanged, this);
339 }
340 const auto label = safenew VST3ValueText(this, wxID_ANY, parameterInfo.id, VST3Utils::ToWxString(parameterInfo.units));
341 sizer->Add(label);
343 }
344 }
345
346 SetSizer(sizer.release());
347}
348
350{
351 for(auto& p : mControls)
352 p.second->SetNormalizedValue(*mEditController, mEditController->getParamNormalized(p.second->GetParameterId()));
353 for(auto& p : mLabels)
354 p.second->SetNormalizedValue(*mEditController, mEditController->getParamNormalized(p.second->GetParameterId()));
355}
356
357void VST3ParametersWindow::UpdateParameter(Steinberg::Vst::ParamID paramId)
358{
359 {
360 auto it = mControls.find(paramId);
361 if(it != mControls.end())
362 it->second->SetNormalizedValue(*mEditController, mEditController->getParamNormalized(it->second->GetParameterId()));
363 }
364 {
365 auto it = mLabels.find(paramId);
366 if(it != mLabels.end())
367 it->second->SetNormalizedValue(*mEditController, mEditController->getParamNormalized(it->second->GetParameterId()));
368 }
369}
370
372{
373 mControls[control->GetParameterId()] = control;
374 control->SetNormalizedValue(*mEditController, mEditController->getParamNormalized(control->GetParameterId()));
375}
376
378{
379 mLabels[label->GetParameterId()] = label;
380 label->SetNormalizedValue(*mEditController, mEditController->getParamNormalized(label->GetParameterId()));
381}
382
384{
385 if(auto control = dynamic_cast<VST3ParameterControl*>(evt.GetEventObject()))
386 {
387 const auto paramId = control->GetParameterId();
388 const auto normalizedValue = control->GetNormalizedValue(*mEditController);
389
390 mEditController->setParamNormalized(paramId, normalizedValue);
391 if(mComponentHandler->beginEdit(paramId) == Steinberg::kResultOk)
392 {
393 auto cleanup = finally([=] { mComponentHandler->endEdit(paramId); });
394 mComponentHandler->performEdit(paramId, normalizedValue);
395 }
396 auto it = mLabels.find(paramId);
397 if (it != mLabels.end()) {
398 it->second->SetNormalizedValue(*mEditController, normalizedValue);
399 control->UpdateAccessible(*mEditController, normalizedValue);
400 }
401 }
402}
403
404VST3ParametersWindow* VST3ParametersWindow::Setup(wxWindow& parent, Steinberg::Vst::IEditController& editController,
405 Steinberg::Vst::IComponentHandler& componentHandler)
406{
407 constexpr int WindowBorder { 5 };
408 constexpr int WindowMaxHeight { 450 };
409
410 auto parametersWindow = safenew VST3ParametersWindow(&parent,
411 editController,
412 componentHandler,
413 wxID_ANY,
414 wxDefaultPosition,
415 wxDefaultSize,
416 wxVSCROLL | wxTAB_TRAVERSAL);
417 // This fools NVDA into not saying "Panel" when the dialog gets focus
418 parametersWindow->SetName(wxT("\a"));
419 parametersWindow->SetLabel(wxT("\a"));
420
421 auto mainSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
422
423 mainSizer->Add(parametersWindow, 1, wxEXPAND | wxALL, WindowBorder);
424
425 auto minSize = parametersWindow->GetSizer()->CalcMin();
426 if(minSize.GetHeight() > (WindowMaxHeight - WindowBorder * 2))
427 {
428 minSize.SetHeight(WindowMaxHeight);
429 parametersWindow->SetScrollRate(0, 20);
430 }
431 else
432 minSize.y += WindowBorder * 2;
433
434 parent.SetMinSize(minSize);
435 parent.SetSizer(mainSizer.release());
436
437 return parametersWindow;
438}
wxT("CloseDown"))
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:74
#define safenew
Definition: MemoryX.h:10
static const auto title
TranslatableString label
Definition: TagsEditor.cpp:164
int id
virtual void UpdateAccessible(Steinberg::Vst::IEditController &editController, Steinberg::Vst::ParamValue value)
Steinberg::Vst::ParamID GetParameterId() const noexcept
VST3ParameterControl(Steinberg::Vst::ParamID id)
const Steinberg::Vst::ParamID mParameterId
virtual Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController &editController) const =0
virtual void SetNormalizedValue(Steinberg::Vst::IEditController &, Steinberg::Vst::ParamValue value)=0
"Plain" plugin UI, contains a list of parameter controls and values.
void OnParameterValueChanged(const wxCommandEvent &evt)
void RegisterParameterLabel(VST3ParameterControl *label)
std::unordered_map< Steinberg::Vst::ParamID, VST3ParameterControl * > mControls
void UpdateParameter(Steinberg::Vst::ParamID paramId)
const Steinberg::IPtr< Steinberg::Vst::IComponentHandler > mComponentHandler
VST3ParametersWindow(wxWindow *parent, Steinberg::Vst::IEditController &editController, Steinberg::Vst::IComponentHandler &handler, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxScrolledWindowStyle, const wxString &name=wxPanelNameStr)
static VST3ParametersWindow * Setup(wxWindow &parent, Steinberg::Vst::IEditController &editController, Steinberg::Vst::IComponentHandler &componentHandler)
Creates VST3ParametersWindow inside parent.
std::unordered_map< Steinberg::Vst::ParamID, VST3ParameterControl * > mLabels
void RegisterParameterControl(VST3ParameterControl *control)
const Steinberg::IPtr< Steinberg::Vst::IEditController > mEditController
static wxString ToWxString(const Steinberg::Vst::TChar *str)
Definition: VST3Utils.cpp:48
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController &) const override
VST3ContinuousParameter(wxWindow *parent, wxWindowID id, Steinberg::Vst::ParamID paramId, const wxString &title, const wxString &units, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxSL_HORIZONTAL, const wxValidator &validator=wxDefaultValidator, const wxString &name=wxSliderNameStr)
void UpdateAccessible(Steinberg::Vst::IEditController &editController, Steinberg::Vst::ParamValue value) override
void SetNormalizedValue(Steinberg::Vst::IEditController &editController, Steinberg::Vst::ParamValue value) override
Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController &editController) const override
void SetNormalizedValue(Steinberg::Vst::IEditController &editController, Steinberg::Vst::ParamValue value) override
void UpdateAccessible(Steinberg::Vst::IEditController &editController, Steinberg::Vst::ParamValue value) override
VST3DiscreteParameter(wxWindow *parent, wxWindowID id, Steinberg::Vst::ParamID paramId, int maxValue, const wxString &title, const wxString &units, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxSL_HORIZONTAL, const wxValidator &validator=wxDefaultValidator, const wxString &name=wxSliderNameStr)
Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController &editController) const override
void SetNormalizedValue(Steinberg::Vst::IEditController &editController, Steinberg::Vst::ParamValue value) override
VST3ListParameter(wxWindow *parent, wxWindowID id, Steinberg::Vst::ParamID paramId, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0, const wxValidator &validator=wxDefaultValidator, const wxString &name=wxChoiceNameStr)
void SetNormalizedValue(Steinberg::Vst::IEditController &editController, Steinberg::Vst::ParamValue value) override
VST3ToggleParameter(wxWindow *parent, wxWindowID id, const wxString &label, Steinberg::Vst::ParamID paramId, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxALIGN_RIGHT, const wxValidator &validator=wxDefaultValidator, const wxString &name=wxCheckBoxNameStr)
Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController &) const override
Steinberg::Vst::ParamValue GetNormalizedValue(Steinberg::Vst::IEditController &editController) const override
VST3ValueText(wxWindow *parent, wxWindowID id, Steinberg::Vst::ParamID paramId, const wxString &units, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0, const wxString &name=wxStaticTextNameStr)
void SetNormalizedValue(Steinberg::Vst::IEditController &editController, Steinberg::Vst::ParamValue value) override