iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
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
22using namespace iplug;
23
24#pragma mark - Shape
25
26double IParam::ShapeLinear::NormalizedToValue(double value, const IParam& param) const
27{
28 return param.mMin + value * (param.mMax - param.mMin);
29}
30
31double IParam::ShapeLinear::ValueToNormalized(double value, const IParam& param) const
32{
33 return (value - param.mMin) / (param.mMax - param.mMin);
34}
35
36IParam::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
51double IParam::ShapePowCurve::NormalizedToValue(double value, const IParam& param) const
52{
53 return param.GetMin() + std::pow(value, mShape) * (param.GetMax() - param.GetMin());
54}
55
56double 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
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
72double IParam::ShapeExp::NormalizedToValue(double value, const IParam& param) const
73{
74 return std::exp(mAdd + value * mMul);
75}
76
77double IParam::ShapeExp::ValueToNormalized(double value, const IParam& param) const
78{
79 return (std::log(value) - mAdd) / mMul;
80}
81
82#pragma mark -
83
84IParam::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
92void 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
102void 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
120void 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
133void 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
140void 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
173void 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
178void 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
183void 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
188void 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
199void 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
204void 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
209void 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
214void 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
248void 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
258{
259 mDisplayPrecision = precision;
260}
261
262void 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 snprintf(fmt, 16, "%%+.%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
307const char* IParam::GetName() const
308{
309 return mName;
310}
311
312const char* IParam::GetLabel() const
313{
314 return (CStringHasContents(GetDisplayText(static_cast<int>(mValue.load())))) ? "" : mLabel;
315}
316
317const char* IParam::GetGroup() const
318{
319 return mParamGroup;
320}
321
323{
324 return mDisplayTexts.GetSize();
325}
326
327const 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
337const 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
344bool 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
358double 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
380void IParam::GetBounds(double& lo, double& hi) const
381{
382 lo = mMin;
383 hi = mMax;
384}
385
386void 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, "\"display_type\":%i, ", mShape->GetDisplayType());
414 json.AppendFormatted(8192, "\"rate\":\"control\"");
415 json.AppendFormatted(8192, "}");
416}
417
419{
420 DBGMSG("%s %f", GetName(), Value());
421}
422
424{
425 if (dynamic_cast<IParam::ShapeLinear*>(mShape.get()))
426 return IParam::EShapeIDs::kShapeLinear;
427 else if (dynamic_cast<IParam::ShapePowCurve*>(mShape.get()))
428 return IParam::EShapeIDs::kShapePowCurve;
429 else if (dynamic_cast<IParam::ShapeExp*>(mShape.get()))
430 return IParam::EShapeIDs::kShapeExponential;
431 else
432 return IParam::EShapeIDs::kShapeUnknown;
433}
434
436{
437 if (auto* pShapePowCurve = dynamic_cast<IParam::ShapePowCurve*>(mShape.get()))
438 return pShapePowCurve->mShape;
439 else
440 return 0.0;
441}
442
IPlug logging a.k.a tracing functionality.
IPlug's parameter class.
double GetDefault(bool normalized=false) const
Returns the parameter's default value.
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.
EParamType Type() const
Get the parameter's type.
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.
@ kFlagSignDisplay
Indicates that the parameter should be displayed as a signed value.
@ kFlagStepped
Indicates that the parameter is stepped
@ kFlagNegateDisplay
Indicates that the parameter should be displayed as a negative value.
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.
void GetDisplay(WDL_String &display, bool withDisplayText=true) const
Get the current textual display for the current parameter value.
double GetMin() const
Returns the parameter's minimum value.
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.
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 GetBounds(double &lo, double &hi) const
Get the minimum and maximum real value of the parameter's range in one method call.
void Set(double value)
Sets the parameter value.
const char * GetLabel() const
Returns the parameter's label.
void SetDisplayText(double value, const char *str)
Set some text to display for a particular value, e.g.
std::function< void(double, WDL_String &)> DisplayFunc
DisplayFunc allows custom parameter display functions, defined by a lambda matching this signature.
EShapeIDs
IDs for the shapes.
double StringToValue(const char *str) const
Convert a textual representation of the parameter value to a double (real value)
bool MapDisplayText(const char *str, double *pValue) const
Get the value of a particular display text.
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.
EParamUnit
Used by AudioUnit plugins to determine the appearance of parameters, based on the kind of data they r...
const char * GetName() const
Returns the parameter's name.
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.
double FromNormalized(double normalizedValue) const
Convert a normalized value to real value for this parameter.
void PrintDetails() const
Helper to print the parameter details to debug console in debug builds.
const char * GetDisplayTextAtIdx(int idx, double *pValue=nullptr) const
Get the display text at a particular index.
double Constrain(double value) const
Constrains the input value between mMin and mMax and apply stepping if relevant.
const char * GetDisplayText(double value) const
Get the display text for a particular value.
const char * GetGroup() const
Returns the parameter's group.
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.
void SetDisplayPrecision(int precision)
Set the parameters display precision.
EDisplayType
Used by AudioUnit plugins to determine the mapping of parameters.
double GetShapeValue() const
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.
double Value() const
Gets a readable value of the parameter.
int NDisplayTexts() const
Get the number of display texts for the parameter.
EShapeIDs GetShapeID() const
double GetMax() const
Returns the parameter's maximum value.
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 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 GetJSON(WDL_String &json, int idx) const
Get a JSON description of the parameter.
static void MidiNoteName(double midiPitch, WDL_String &noteName, bool cents=false, bool middleCisC4=false)
Exponential parameter shaping.
void Init(const IParam &param) override
Initializes the shape instance.
double NormalizedToValue(double value, const IParam &param) const override
Returns the real value from a normalized input, based on an IParam's settings.
double ValueToNormalized(double value, const IParam &param) const override
Returns the normalized value from a real value, based on an IParam's settings.
Base struct for parameter shaping.
virtual Shape * Clone() const =0
Linear parameter shaping.
double ValueToNormalized(double value, const IParam &param) const override
Returns the normalized value from a real value, based on an IParam's settings.
double NormalizedToValue(double value, const IParam &param) const override
Returns the real value from a normalized input, based on an IParam's settings.
PowCurve parameter shaping.
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's settings.
double NormalizedToValue(double value, const IParam &param) const override
Returns the real value from a normalized input, based on an IParam's settings.