iPlug2 - C++ Audio Plug-in Framework
IPlugParameter.cpp
Go to the documentation of this file.
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 
16 #include <cstdio>
17 #include <algorithm>
18 
19 #include "IPlugParameter.h"
20 #include "IPlugLogger.h"
21 
22 using namespace iplug;
23 
24 #pragma mark - Shape
25 
26 double IParam::ShapeLinear::NormalizedToValue(double value, const IParam& param) const
27 {
28  return param.mMin + value * (param.mMax - param.mMin);
29 }
30 
31 double IParam::ShapeLinear::ValueToNormalized(double value, const IParam& param) const
32 {
33  return (value - param.mMin) / (param.mMax - param.mMin);
34 }
35 
36 IParam::ShapePowCurve::ShapePowCurve(double shape)
37 : mShape(shape)
38 {
39 }
40 
42 {
43  if (mShape > 2.5) return kDisplayCubeRoot;
44  if (mShape > 1.5) return kDisplaySquareRoot;
45  if (mShape < (2.0 / 5.0)) return kDisplayCubed;
46  if (mShape < (2.0 / 3.0)) return kDisplaySquared;
47 
48  return IParam::kDisplayLinear;
49 }
50 
51 double IParam::ShapePowCurve::NormalizedToValue(double value, const IParam& param) const
52 {
53  return param.GetMin() + std::pow(value, mShape) * (param.GetMax() - param.GetMin());
54 }
55 
56 double IParam::ShapePowCurve::ValueToNormalized(double value, const IParam& param) const
57 {
58  return std::pow((value - param.GetMin()) / (param.GetMax() - param.GetMin()), 1.0 / mShape);
59 }
60 
61 void IParam::ShapeExp::Init(const IParam& param)
62 {
63  double min = param.GetMin();
64 
65  if(min <= 0.)
66  min = 0.00000001;
67 
68  mAdd = std::log(min);
69  mMul = std::log(param.GetMax() / min);
70 }
71 
72 double IParam::ShapeExp::NormalizedToValue(double value, const IParam& param) const
73 {
74  return std::exp(mAdd + value * mMul);
75 }
76 
77 double IParam::ShapeExp::ValueToNormalized(double value, const IParam& param) const
78 {
79  return (std::log(value) - mAdd) / mMul;
80 }
81 
82 #pragma mark -
83 
84 IParam::IParam()
85 {
86  mShape = std::make_unique<ShapeLinear>();
87  memset(mName, 0, MAX_PARAM_NAME_LEN * sizeof(char));
88  memset(mLabel, 0, MAX_PARAM_LABEL_LEN * sizeof(char));
89  memset(mParamGroup, 0, MAX_PARAM_LABEL_LEN * sizeof(char));
90 };
91 
92 void IParam::InitBool(const char* name, bool defaultVal, const char* label, int flags, const char* group, const char* offText, const char* onText)
93 {
94  if (mType == kTypeNone) mType = kTypeBool;
95 
96  InitEnum(name, (defaultVal ? 1 : 0), 2, label, flags | kFlagStepped, group);
97 
98  SetDisplayText(0, offText);
99  SetDisplayText(1, onText);
100 }
101 
102 void IParam::InitEnum(const char* name, int defaultVal, int nEnums, const char* label, int flags, const char* group, const char* listItems, ...)
103 {
104  if (mType == kTypeNone) mType = kTypeEnum;
105 
106  InitInt(name, defaultVal, 0, nEnums - 1, label, flags | kFlagStepped, group);
107 
108  if(listItems)
109  {
110  SetDisplayText(0, listItems);
111 
112  va_list args;
113  va_start(args, listItems);
114  for (auto i = 1; i < nEnums; ++i)
115  SetDisplayText(i, va_arg(args, const char*));
116  va_end(args);
117  }
118 }
119 
120 void IParam::InitEnum(const char* name, int defaultVal, const std::initializer_list<const char*>& listItems, int flags, const char* group)
121 {
122  if (mType == kTypeNone) mType = kTypeEnum;
123 
124  InitInt(name, defaultVal, 0, static_cast<int>(listItems.size()) - 1, "", flags | kFlagStepped, group);
125 
126  int idx = 0;
127  for (auto& item : listItems)
128  {
129  SetDisplayText(idx++, item);
130  }
131 }
132 
133 void IParam::InitInt(const char* name, int defaultVal, int minVal, int maxVal, const char* label, int flags, const char* group)
134 {
135  if (mType == kTypeNone) mType = kTypeInt;
136 
137  InitDouble(name, (double) defaultVal, (double) minVal, (double) maxVal, 1.0, label, flags | kFlagStepped, group);
138 }
139 
140 void IParam::InitDouble(const char* name, double defaultVal, double minVal, double maxVal, double step, const char* label, int flags, const char* group, const Shape& shape, EParamUnit unit, DisplayFunc displayFunc)
141 {
142  if (mType == kTypeNone) mType = kTypeDouble;
143 
144 // assert(CStringHasContents(mName) && "Parameter already initialised!");
145 // assert(CStringHasContents(name) && "Parameter must be given a name!");
146 
147  strcpy(mName, name);
148  strcpy(mLabel, label);
149  strcpy(mParamGroup, group);
150 
151  // N.B. apply stepping and constraints to the default value (and store the result)
152  mMin = minVal;
153  mMax = std::max(maxVal, minVal + step);
154  mStep = step;
155  mDefault = defaultVal;
156  mUnit = unit;
157  mFlags = flags;
158  mDisplayFunction = displayFunc;
159 
160  Set(defaultVal);
161 
162  for (mDisplayPrecision = 0;
163  mDisplayPrecision < MAX_PARAM_DISPLAY_PRECISION && step != floor(step);
164  ++mDisplayPrecision, step *= 10.0)
165  {
166  ;
167  }
168 
169  mShape = std::unique_ptr<Shape>(shape.Clone());
170  mShape->Init(*this);
171 }
172 
173 void IParam::InitFrequency(const char *name, double defaultVal, double minVal, double maxVal, double step, int flags, const char *group)
174 {
175  InitDouble(name, defaultVal, minVal, maxVal, step, "Hz", flags, group, ShapeExp(), kUnitFrequency);
176 }
177 
178 void IParam::InitSeconds(const char *name, double defaultVal, double minVal, double maxVal, double step, int flags, const char *group)
179 {
180  InitDouble(name, defaultVal, minVal, maxVal, step, "Seconds", flags, group, ShapeLinear(), kUnitSeconds);
181 }
182 
183 void IParam::InitMilliseconds(const char *name, double defaultVal, double minVal, double maxVal, int flags, const char *group)
184 {
185  InitDouble(name, defaultVal, minVal, maxVal, 1, "ms", flags, group, ShapeLinear(), kUnitMilliseconds);
186 }
187 
188 void IParam::InitPitch(const char *name, int defaultVal, int minVal, int maxVal, int flags, const char *group, bool middleCisC)
189 {
190  InitEnum(name, defaultVal, (maxVal - minVal) + 1, "", flags, group);
191  WDL_String displayText;
192  for (auto i = minVal; i <= maxVal; i++)
193  {
194  MidiNoteName(i, displayText, /*cents*/false, middleCisC);
195  SetDisplayText(i - minVal, displayText.Get());
196  }
197 }
198 
199 void IParam::InitGain(const char *name, double defaultVal, double minVal, double maxVal, double step, int flags, const char *group)
200 {
201  InitDouble(name, defaultVal, minVal, maxVal, step, "dB", flags, group, ShapeLinear(), kUnitDB);
202 }
203 
204 void IParam::InitPercentage(const char *name, double defaultVal, double minVal, double maxVal, int flags, const char *group)
205 {
206  InitDouble(name, defaultVal, minVal, maxVal, 1, "%", flags, group, ShapeLinear(), kUnitPercentage);
207 }
208 
209 void IParam::InitAngleDegrees(const char *name, double defaultVal, double minVal, double maxVal, int flags, const char *group)
210 {
211  InitDouble(name, defaultVal, minVal, maxVal, 1, "degrees", flags, group, ShapeLinear(), kUnitDegrees);
212 }
213 
214 void IParam::Init(const IParam& p, const char* searchStr, const char* replaceStr, const char* newGroup)
215 {
216  if (mType == kTypeNone) mType = p.Type();
217 
218  WDL_String str(p.mName);
219  WDL_String group(p.mParamGroup);
220 
221  if (CStringHasContents(searchStr))
222  {
223  char* pos = strstr(str.Get(), searchStr);
224 
225  if(pos)
226  {
227  int insertionPos = static_cast<int>(str.Get() - pos);
228  str.DeleteSub(insertionPos, static_cast<int>(strlen(searchStr)));
229  str.Insert(replaceStr, insertionPos);
230  }
231  }
232 
233  if (CStringHasContents(newGroup))
234  {
235  group.Set(newGroup);
236  }
237 
238  InitDouble(str.Get(), p.mDefault, p.mMin, p.mMax, p.mStep, p.mLabel, p.mFlags, group.Get(), *p.mShape, p.mUnit, p.mDisplayFunction);
239 
240  for (auto i=0; i<p.NDisplayTexts(); i++)
241  {
242  double val;
243  const char* str = p.GetDisplayTextAtIdx(i, &val);
244  SetDisplayText(val, str);
245  }
246 }
247 
248 void IParam::SetDisplayText(double value, const char* str)
249 {
250  int n = mDisplayTexts.GetSize();
251  mDisplayTexts.Resize(n + 1);
252  DisplayText* pDT = mDisplayTexts.Get() + n;
253  pDT->mValue = value;
254  strcpy(pDT->mText, str);
255 }
256 
257 void IParam::SetDisplayPrecision(int precision)
258 {
259  mDisplayPrecision = precision;
260 }
261 
262 void IParam::GetDisplay(double value, bool normalized, WDL_String& str, bool withDisplayText) const
263 {
264  if (normalized) value = FromNormalized(value);
265 
266  if (mDisplayFunction != nullptr)
267  {
268  mDisplayFunction(value, str);
269  return;
270  }
271 
272  if (withDisplayText)
273  {
274  const char* displayText = GetDisplayText(value);
275 
276  if (CStringHasContents(displayText))
277  {
278  str.Set(displayText, MAX_PARAM_DISPLAY_LEN);
279  return;
280  }
281  }
282 
283  double displayValue = value;
284 
285  if (mFlags & kFlagNegateDisplay)
286  displayValue = -displayValue;
287 
288  // Squash all zeros to positive
289  if (!displayValue) displayValue = 0.0;
290 
291  if (mDisplayPrecision == 0)
292  {
293  str.SetFormatted(MAX_PARAM_DISPLAY_LEN, "%d", static_cast<int>(round(displayValue)));
294  }
295  else if ((mFlags & kFlagSignDisplay) && displayValue)
296  {
297  char fmt[16];
298  sprintf(fmt, "%%+.%df", mDisplayPrecision);
299  str.SetFormatted(MAX_PARAM_DISPLAY_LEN, fmt, displayValue);
300  }
301  else
302  {
303  str.SetFormatted(MAX_PARAM_DISPLAY_LEN, "%.*f", mDisplayPrecision, displayValue);
304  }
305 }
306 
307 const char* IParam::GetName() const
308 {
309  return mName;
310 }
311 
312 const char* IParam::GetLabel() const
313 {
314  return (CStringHasContents(GetDisplayText(static_cast<int>(mValue.load())))) ? "" : mLabel;
315 }
316 
317 const char* IParam::GetGroup() const
318 {
319  return mParamGroup;
320 }
321 
323 {
324  return mDisplayTexts.GetSize();
325 }
326 
327 const char* IParam::GetDisplayText(double value) const
328 {
329  int n = mDisplayTexts.GetSize();
330  for (DisplayText* pDT = mDisplayTexts.Get(); n; --n, ++pDT)
331  {
332  if (value == pDT->mValue) return pDT->mText;
333  }
334  return "";
335 }
336 
337 const char* IParam::GetDisplayTextAtIdx(int idx, double* pValue) const
338 {
339  DisplayText* pDT = mDisplayTexts.Get()+idx;
340  if (pValue) *pValue = pDT->mValue;
341  return pDT->mText;
342 }
343 
344 bool IParam::MapDisplayText(const char* str, double* pValue) const
345 {
346  int n = mDisplayTexts.GetSize();
347  for (DisplayText* pDT = mDisplayTexts.Get(); n; --n, ++pDT)
348  {
349  if (!strcmp(str, pDT->mText))
350  {
351  *pValue = pDT->mValue;
352  return true;
353  }
354  }
355  return false;
356 }
357 
358 double IParam::StringToValue(const char* str) const
359 {
360  double v = 0.;
361  bool mapped = (bool) NDisplayTexts();
362 
363  if (mapped)
364  mapped = MapDisplayText(str, &v);
365 
366  if (!mapped && Type() != kTypeEnum && Type() != kTypeBool)
367  {
368  v = atof(str);
369 
370  if (mFlags & kFlagNegateDisplay)
371  v = -v;
372 
373  v = Constrain(v);
374  mapped = true;
375  }
376 
377  return v;
378 }
379 
380 void IParam::GetBounds(double& lo, double& hi) const
381 {
382  lo = mMin;
383  hi = mMax;
384 }
385 
386 void IParam::GetJSON(WDL_String& json, int idx) const
387 {
388  json.AppendFormatted(8192, "{");
389  json.AppendFormatted(8192, "\"id\":%i, ", idx);
390  json.AppendFormatted(8192, "\"name\":\"%s\", ", GetName());
391  switch (Type())
392  {
393  case IParam::kTypeNone:
394  break;
395  case IParam::kTypeBool:
396  json.AppendFormatted(8192, "\"type\":\"%s\", ", "bool");
397  break;
398  case IParam::kTypeInt:
399  json.AppendFormatted(8192, "\"type\":\"%s\", ", "int");
400  break;
401  case IParam::kTypeEnum:
402  json.AppendFormatted(8192, "\"type\":\"%s\", ", "enum");
403  break;
404  case IParam::kTypeDouble:
405  json.AppendFormatted(8192, "\"type\":\"%s\", ", "float");
406  break;
407  default:
408  break;
409  }
410  json.AppendFormatted(8192, "\"min\":%f, ", GetMin());
411  json.AppendFormatted(8192, "\"max\":%f, ", GetMax());
412  json.AppendFormatted(8192, "\"default\":%f, ", GetDefault());
413  json.AppendFormatted(8192, "\"rate\":\"control\"");
414  json.AppendFormatted(8192, "}");
415 }
416 
418 {
419  DBGMSG("%s %f", GetName(), Value());
420 }
void InitSeconds(const char *name, double defaultVal=1., double minVal=0., double maxVal=10., double step=0.1, int flags=0, const char *group="")
Initialize the parameter as seconds.
void InitPitch(const char *name, int defaultVal=60, int minVal=0, int maxVal=128, int flags=0, const char *group="", bool middleCisC4=false)
Initialize the parameter as pitch.
const char * GetName() const
Returns the parameter&#39;s name.
int NDisplayTexts() const
Get the number of display texts for the parameter.
IParam::EDisplayType GetDisplayType() const override
double ValueToNormalized(double value, const IParam &param) const override
Returns the normalized value from a real value, based on an IParam&#39;s settings.
void GetJSON(WDL_String &json, int idx) const
Get a JSON description of the parameter.
void InitMilliseconds(const char *name, double defaultVal=1., double minVal=0., double maxVal=100., int flags=0, const char *group="")
Initialize the parameter as milliseconds.
IPlug&#39;s parameter class.
const char * GetDisplayText(double value) const
Get the display text for a particular value.
double NormalizedToValue(double value, const IParam &param) const override
Returns the real value from a normalized input, based on an IParam&#39;s settings.
IPlug&#39;s parameter class.
double NormalizedToValue(double value, const IParam &param) const override
Returns the real value from a normalized input, based on an IParam&#39;s settings.
Exponential parameter shaping.
Base struct for parameter shaping.
IPlug logging a.k.a tracing functionality.
void InitPercentage(const char *name, double defaultVal=0., double minVal=0., double maxVal=100., int flags=0, const char *group="")
Initialize the parameter as percentage.
double GetMin() const
Returns the parameter&#39;s minimum value.
void InitFrequency(const char *name, double defaultVal=1000., double minVal=0.1, double maxVal=10000., double step=0.1, int flags=0, const char *group="")
Initialize the parameter as frequency.
static void MidiNoteName(double midiPitch, WDL_String &noteName, bool cents=false, bool middleCisC4=false)
double StringToValue(const char *str) const
Convert a textual representation of the parameter value to a double (real value)
double GetMax() const
Returns the parameter&#39;s maximum value.
std::function< void(double, WDL_String &)> DisplayFunc
DisplayFunc allows custom parameter display functions, defined by a lambda matching this signature...
void InitEnum(const char *name, int defaultValue, int nEnums, const char *label="", int flags=0, const char *group="", const char *listItems=0,...)
Initialize the parameter as an enumerated list.
const char * GetGroup() const
Returns the parameter&#39;s group.
void GetDisplay(WDL_String &display, bool withDisplayText=true) const
Get the current textual display for the current parameter value.
const char * GetLabel() const
Returns the parameter&#39;s label.
double ValueToNormalized(double value, const IParam &param) const override
Returns the normalized value from a real value, based on an IParam&#39;s settings.
void PrintDetails() const
Helper to print the parameter details to debug console in debug builds.
double ValueToNormalized(double value, const IParam &param) const override
Returns the normalized value from a real value, based on an IParam&#39;s settings.
EParamUnit
Used by AudioUnit plugins to determine the appearance of parameters, based on the kind of data they r...
EParamType Type() const
Get the parameter&#39;s type.
void InitBool(const char *name, bool defaultValue, const char *label="", int flags=0, const char *group="", const char *offText="off", const char *onText="on")
Initialize the parameter as boolean.
void SetDisplayPrecision(int precision)
Set the parameters display precision.
void Init(const IParam &param) override
Initializes the shape instance.
void Init(const IParam &p, const char *searchStr="", const char *replaceStr="", const char *newGroup="")
Initialize the parameter based on another parameter, replacing a CString in the name.
void InitInt(const char *name, int defaultValue, int minVal, int maxVal, const char *label="", int flags=0, const char *group="")
Initialize the parameter as integer.
void InitGain(const char *name, double defaultVal=0., double minVal=-70., double maxVal=24., double step=0.5, int flags=0, const char *group="")
Initialize the parameter as gain (units in decibels)
void InitAngleDegrees(const char *name, double defaultVal=0., double minVal=0., double maxVal=360., int flags=0, const char *group="")
Initialize the parameter as angle in degrees.
double NormalizedToValue(double value, const IParam &param) const override
Returns the real value from a normalized input, based on an IParam&#39;s settings.
Linear parameter shaping.
void InitDouble(const char *name, double defaultVal, double minVal, double maxVal, double step, const char *label="", int flags=0, const char *group="", const Shape &shape=ShapeLinear(), EParamUnit unit=kUnitCustom, DisplayFunc displayFunc=nullptr)
Initialize the parameter as double.
bool MapDisplayText(const char *str, double *pValue) const
Get the value of a particular display text.
virtual Shape * Clone() const =0
EDisplayType
Used by AudioUnit plugins to determine the mapping of parameters.
void GetBounds(double &lo, double &hi) const
Get the minimum and maximum real value of the parameter&#39;s range in one method call.
void SetDisplayText(double value, const char *str)
Set some text to display for a particular value, e.g.
const char * GetDisplayTextAtIdx(int idx, double *pValue=nullptr) const
Get the display text at a particular index.