iPlug2 - C++ Audio Plug-in Framework
IPlugVST3_ControllerBase.h
1 /*
2  ==============================================================================
3 
4  This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5 
6  See LICENSE.txt for more info.
7 
8  ==============================================================================
9 */
10 
11 #pragma once
12 
13 #include "pluginterfaces/base/ibstream.h"
14 #include "public.sdk/source/vst/vsteditcontroller.h"
15 #include "pluginterfaces/vst/ivstchannelcontextinfo.h"
16 
17 #include "IPlugAPIBase.h"
18 #include "IPlugVST3_Parameter.h"
19 #include "IPlugVST3_Defs.h"
20 
21 #include "IPlugMidi.h"
22 
23 BEGIN_IPLUG_NAMESPACE
24 
27 {
28 public:
29 
30  IPlugVST3ControllerBase(Steinberg::Vst::ParameterContainer& parameters) : mParameters(parameters) {}
31 
33  IPlugVST3ControllerBase& operator=(const IPlugVST3ControllerBase&) = delete;
34 
35  void Initialize(IPlugAPIBase* pPlug, bool plugIsInstrument, bool midiIn)
36  {
37  Steinberg::Vst::EditControllerEx1* pEditController = dynamic_cast<Steinberg::Vst::EditControllerEx1*>(pPlug);
38 
39  Steinberg::Vst::UnitInfo unitInfo;
40  unitInfo.id = Steinberg::Vst::kRootUnitId;
41  unitInfo.parentUnitId = Steinberg::Vst::kNoParentUnitId;
42  Steinberg::UString unitNameSetter(unitInfo.name, 128);
43  unitNameSetter.fromAscii("Root");
44 
45  Steinberg::Vst::UnitID unitID = Steinberg::Vst::kRootUnitId;
46 
47  #ifdef VST3_PRESET_LIST
48  if (pPlug->NPresets())
49  {
50  unitInfo.programListId = kPresetParam;
51  mParameters.addParameter(new IPlugVST3PresetParameter(pPlug->NPresets()));
52 
53  Steinberg::Vst::ProgramListWithPitchNames* pList = new Steinberg::Vst::ProgramListWithPitchNames(STR16("Factory Presets"), 0 /* list id */, Steinberg::Vst::kRootUnitId);
54 
55  Steinberg::Vst::String128 programName;
56  Steinberg::Vst::String128 pitchName;
57 
58  for (int programIdx=0; programIdx<pPlug->NPresets(); programIdx++)
59  {
60  Steinberg::UString(programName, str16BufferSize(Steinberg::Vst::String128)).assign(pPlug->GetPresetName(programIdx));
61  pList->addProgram (programName);
62 
63  //Set named notes. This could be different per-preset in VST3
64  for (int pitch = 0; pitch < 128; pitch++)
65  {
66  char pNoteText[32] = "";
67  if(pPlug->GetMidiNoteText(pitch, pNoteText))
68  {
69  Steinberg::UString(pitchName, str16BufferSize(Steinberg::Vst::String128)).assign(pNoteText);
70  pList->setPitchName(programIdx, pitch, pitchName);
71  }
72  }
73  }
74 
75  pEditController->addProgramList(pList);
76  }
77  else
78  #endif
79  unitInfo.programListId = Steinberg::Vst::kNoProgramListId;
80 
81  if (!plugIsInstrument)
82  mParameters.addParameter(mBypassParameter = new IPlugVST3BypassParameter());
83 
84  pEditController->addUnit(new Steinberg::Vst::Unit(unitInfo));
85 
86  for (int i = 0; i < pPlug->NParams(); i++)
87  {
88  IParam* pParam = pPlug->GetParam(i);
89  unitID = Steinberg::Vst::kRootUnitId; // reset unitID
90 
91  const char* paramGroupName = pParam->GetGroup();
92 
93  if (CStringHasContents(paramGroupName)) // if the parameter has a group
94  {
95  for (int j = 0; j < pPlug->NParamGroups(); j++) // loop through previously added groups
96  {
97  if (strcmp(paramGroupName, pPlug->GetParamGroupName(j)) == 0) // if group name found in existing groups
98  {
99  unitID = j + 1; // increment unitID
100  }
101  }
102 
103  if (unitID == Steinberg::Vst::kRootUnitId) // if unitID was still kRootUnitId, we found a new group, so add it and add the unit
104  {
105  unitID = pPlug->AddParamGroup(paramGroupName); // updates unitID
106  unitInfo.id = unitID;
107  unitInfo.parentUnitId = Steinberg::Vst::kRootUnitId;
108  unitInfo.programListId = Steinberg::Vst::kNoProgramListId;
109  unitNameSetter.fromAscii(paramGroupName);
110  pEditController->addUnit (new Steinberg::Vst::Unit (unitInfo));
111  }
112  }
113 
114  Steinberg::Vst::Parameter* pVST3Parameter = new IPlugVST3Parameter(pParam, i, unitID);
115  mParameters.addParameter(pVST3Parameter);
116  }
117 
118  assert(VST3_NUM_CC_CHANS <= VST3_NUM_MIDI_IN_CHANS && "VST3_NUM_CC_CHANS must be less than or equal to VST3_NUM_MIDI_IN_CHANS");
119 
120 #if VST3_NUM_CC_CHANS > 0
121  if (midiIn)
122  {
123  unitInfo.id = unitID = pEditController->getUnitCount() + 1;
124  unitInfo.parentUnitId = Steinberg::Vst::kRootUnitId;
125  unitInfo.programListId = Steinberg::Vst::kNoProgramListId;
126  unitNameSetter.fromAscii(VST3_CC_UNITNAME);
127  pEditController->addUnit(new Steinberg::Vst::Unit(unitInfo));
128 
129  Steinberg::Vst::ParamID paramIdx = kMIDICCParamStartIdx;
130 
131  WDL_String chanGroupStr;
132  Steinberg::Vst::UnitID midiCCsUnitID = unitID;
133 
134  for (int chan = 0; chan < VST3_NUM_CC_CHANS; chan++)
135  {
136  chanGroupStr.SetFormatted(32, "CH%i", chan + 1);
137 
138  unitInfo.id = unitID = pEditController->getUnitCount() + 1;
139  unitInfo.parentUnitId = midiCCsUnitID;
140  unitInfo.programListId = Steinberg::Vst::kNoProgramListId;
141  unitNameSetter.fromAscii(chanGroupStr.Get());
142  pEditController->addUnit(new Steinberg::Vst::Unit(unitInfo));
143  // add 128 MIDI CCs
144  Steinberg::Vst::String128 paramName;
145  for (int i = 0; i < 128; i++)
146  {
147  Steinberg::UString(paramName, str16BufferSize(Steinberg::Vst::String128)).assign(IMidiMsg::CCNameStr(i));
148  mParameters.addParameter(paramName, STR16(""), 0, 0, 0, paramIdx++, unitID);
149  }
150 
151  mParameters.addParameter(STR16("Channel Aftertouch"), STR16(""), 0, 0, 0, paramIdx++, unitID);
152  mParameters.addParameter(STR16("Pitch Bend"), STR16(""), 0, 0.5, 0, paramIdx++, unitID);
153  }
154  }
155 #endif
156  }
157 
158  Steinberg::tresult PLUGIN_API GetProgramName(IPlugAPIBase* pPlug, Steinberg::Vst::ProgramListID listId, Steinberg::int32 programIndex, Steinberg::Vst::String128 name)
159  {
160  if (pPlug->NPresets() && listId == kPresetParam)
161  {
162  Steinberg::UString(name, 128).fromAscii(pPlug->GetPresetName(programIndex));
163  return Steinberg::kResultTrue;
164  }
165 
166  return Steinberg::kResultFalse;
167  }
168 
169  Steinberg::int32 PLUGIN_API GetProgramListCount(IPlugAPIBase* pPlug)
170  {
171 #ifdef VST3_PRESET_LIST
172  return (pPlug->NPresets() > 0);
173 #else
174  return 0;
175 #endif
176  }
177 
178  Steinberg::tresult PLUGIN_API GetProgramListInfo(IPlugAPIBase* pPlug, Steinberg::int32 listIndex, Steinberg::Vst::ProgramListInfo& info)
179  {
180 #ifdef VST3_PRESET_LIST
181  if (listIndex == 0 && pPlug->NPresets() > 0)
182  {
183  info.id = kPresetParam;
184  info.programCount = (Steinberg::int32) pPlug->NPresets();
185  Steinberg::UString name(info.name, 128);
186  name.fromAscii("Factory Presets");
187  return Steinberg::kResultTrue;
188  }
189 #endif
190 
191  return Steinberg::kResultFalse;
192  }
193 
194  Steinberg::Vst::ParamValue GetParamNormalized(Steinberg::Vst::ParamID tag)
195  {
196  Steinberg::Vst::Parameter* parameter = mParameters.getParameter(tag);
197  return parameter ? parameter->getNormalized() : 0.0;
198  }
199 
200  bool SetParamNormalized(IPlugAPIBase* pPlug, Steinberg::Vst::ParamID tag, Steinberg::Vst::ParamValue value)
201  {
202  if (!SetVST3ParamNormalized(tag, value))
203  return false;
204 
205  if (tag >= kBypassParam)
206  {
207  switch (tag)
208  {
209  case kPresetParam:
210  {
211  pPlug->RestorePreset(std::round((pPlug->NPresets() - 1) * value));
212  break;
213  }
214  default:
215  break;
216  }
217  }
218  else
219  {
220  IParam* pParam = pPlug->GetParam(tag);
221 
222  if (pParam)
223  {
224  pParam->SetNormalized(value);
225  pPlug->OnParamChangeUI(tag, kHost);
226  pPlug->SendParameterValueFromDelegate(tag, value, true);
227  }
228  }
229 
230  return true;
231  }
232 
233  bool SetChannelContextInfos(Steinberg::Vst::IAttributeList* pList)
234  {
235  using namespace Steinberg;
236  using namespace Vst;
237 
238  if (pList)
239  {
240  // optional we can ask for the channel name length
241  int64 length;
242  if (pList->getInt(ChannelContext::kChannelNameLengthKey, length) == kResultTrue)
243  {
244  // get the channel name where we, as plug-in, are instantiated
245  // Note: length is multiplied by two because Ableton Live 10.1.13 is buggy
246  // and pList->getString() size parameter is interpreted as TChar instead
247  // of byte: end of string zero value is written in an out of memory position
248  std::vector<TChar> name((length+1)*2);
249  if (pList->getString(ChannelContext::kChannelNameKey, name.data(), static_cast<Steinberg::uint32>(length+1)*sizeof(TChar)) == kResultTrue)
250  {
251  Steinberg::String str(name.data());
252  str.toMultiByte(kCP_Utf8);
253  mChannelName.Set(str);
254  }
255  }
256 
257  // get the channel uid namespace length
258  if (pList->getInt(ChannelContext::kChannelUIDLengthKey, length) == kResultTrue)
259  {
260  // get the channel UID
261  // Note: length is multiplied by two because Ableton Live 10.1.13 is buggy
262  // and pList->getString() size parameter is interpreted as TChar instead
263  // of byte: end of string zero value is written in an out of memory position
264  std::vector<TChar> name((length+1)*2);
265  if (pList->getString(ChannelContext::kChannelUIDKey, name.data(), static_cast<Steinberg::uint32>(length+1)*sizeof(TChar)) == kResultTrue)
266  {
267  Steinberg::String str(name.data());
268  str.toMultiByte(kCP_Utf8);
269  mChannelUID.Set(str);
270  }
271  }
272 
273  // get channel index
274  int64 index;
275  if (pList->getInt(ChannelContext::kChannelIndexKey, index) == kResultTrue)
276  {
277  mChannelIndex = static_cast<int>(index);
278  }
279 
280  // get the channel color
281  int64 color;
282  if (pList->getInt(ChannelContext::kChannelColorKey, color) == kResultTrue)
283  {
284  mChannelColor = (uint32) color;
285  }
286 
287  // get channel index namespace order of the current used index namespace
288  if (pList->getInt(ChannelContext::kChannelIndexNamespaceOrderKey, index) == kResultTrue)
289  {
290  mChannelNamespaceIndex = static_cast<int>(index);
291  }
292 
293  // get the channel index namespace length
294  if (pList->getInt(ChannelContext::kChannelIndexNamespaceLengthKey, length) == kResultTrue)
295  {
296  // get the channel index namespace
297  // Note: length is multiplied by two because Ableton Live 10.1.13 is buggy
298  // and pList->getString() size parameter is interpreted as TChar instead
299  // of byte: end of string zero value is written in an out of memory position
300  std::vector<TChar> name((length+1)*2);
301  if (pList->getString(ChannelContext::kChannelIndexNamespaceKey, name.data(), static_cast<Steinberg::uint32>(length+1)*sizeof(TChar)) == kResultTrue)
302  {
303  Steinberg::String str(name.data());
304  str.toMultiByte(kCP_Utf8);
305  mChannelNamespace.Set(str);
306  }
307  }
308 
309  // get plug-in channel location
310  int64 location;
311  if (pList->getInt(ChannelContext::kChannelPluginLocationKey, location) == kResultTrue)
312  {
313  String128 string128;
314  switch (location)
315  {
316  case ChannelContext::kPreVolumeFader:
317  Steinberg::UString(string128, 128).fromAscii("PreVolFader");
318  break;
319  case ChannelContext::kPostVolumeFader:
320  Steinberg::UString(string128, 128).fromAscii("PostVolFader");
321  break;
322  case ChannelContext::kUsedAsPanner:
323  Steinberg::UString(string128, 128).fromAscii("UsedAsPanner");
324  break;
325  default: Steinberg::UString(string128, 128).fromAscii("unknown!");
326  break;
327  }
328  }
329 
330  return true;
331  }
332 
333  return false;
334  }
335 
336  void UpdateParams(IPlugAPIBase* pPlug, int savedBypass)
337  {
338  for (int i = 0; i < pPlug->NParams(); i++)
339  {
340  double normalized = pPlug->GetParam(i)->GetNormalized();
341  mParameters.getParameter(i)->setNormalized(normalized);
342  }
343 
344  if (mBypassParameter)
345  mBypassParameter->setNormalized(savedBypass);
346  }
347 
348 protected:
349 
350  bool SetVST3ParamNormalized(Steinberg::Vst::ParamID tag, Steinberg::Vst::ParamValue value)
351  {
352  Steinberg::Vst::Parameter* parameter = mParameters.getParameter(tag);
353 
354  if (!parameter)
355  return false;
356 
357  parameter->setNormalized(value);
358  return true;
359  }
360 
361 public:
362  Steinberg::Vst::ParameterContainer& mParameters;
363  IPlugVST3BypassParameter* mBypassParameter = nullptr;
364 
365  // ChannelContext::IInfoListener
366  WDL_String mChannelName;
367  WDL_String mChannelNamespace;
368  WDL_String mChannelUID;
369  int mChannelNamespaceIndex = 0;
370  int mChannelIndex = 0;
371  unsigned int mChannelColor = 0;
372 };
373 
374 END_IPLUG_NAMESPACE
const char * GetPresetName(int idx) const
Get the name a preset.
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
Definition: IPlugAPIBase.h:42
int NPresets() const
Gets the number of factory presets.
Shared VST3 controller code.
IPlug&#39;s parameter class.
virtual bool GetMidiNoteText(int noteNumber, char *str) const
Override this method to provide custom text linked to MIDI note numbers in API classes that support t...
Definition: IPlugAPIBase.h:92
const char * GetParamGroupName(int idx) const
Get the parameter group name as a particular index.
void SetNormalized(double normalizedValue)
Sets the parameter value from a normalized range (usually coming from the linked IControl) ...
const char * GetGroup() const
Returns the parameter&#39;s group.
static const char * CCNameStr(int idx)
Get the CC name as a CString.
Definition: IPlugMidi.h:386
VST3 preset parameter helper.
bool RestorePreset(int idx)
Restore a preset by index.
VST3 bypass parameter helper.
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
int AddParamGroup(const char *name)
Called to add a parameter group name, when a unique group name is discovered.
MIDI and sysex structs/utilites.
int NParamGroups() const