1 /*
2 ** Surge Synthesizer is Free and Open Source Software
3 **
4 ** Surge is made available under the Gnu General Public License, v3.0
5 ** https://www.gnu.org/licenses/gpl-3.0.en.html
6 **
7 ** Copyright 2004-2020 by various individuals as described by the Git transaction log
8 **
9 ** All source at: https://github.com/surge-synthesizer/surge.git
10 **
11 ** Surge was a commercial product from 2004-2018, with Copyright and ownership
12 ** in that period held by Claes Johanson at Vember Audio. Claes made Surge
13 ** open source in September 2018.
14 */
15 
16 #pragma once
17 #include "globals.h"
18 #include <string>
19 #include <memory>
20 #include <cstdint>
21 #include <functional>
22 #include <atomic>
23 #include "SkinModel.h"
24 
25 union pdata
26 {
27     int i;
28     bool b;
29     float f;
30 };
31 
32 enum valtypes
33 {
34     vt_int = 0,
35     vt_bool,
36     vt_float,
37 };
38 
39 enum ctrltypes
40 {
41     ct_none,
42     ct_percent,
43     ct_percent_deactivatable,
44     ct_percent_bipolar,
45     ct_percent_bipolar_stereo,    // bipolar with special text strings at -100% +100% and 0%
46     ct_percent_bipolar_stringbal, // bipolar with special text strings
47     ct_percent_bipolar_w_dynamic_unipolar_formatting,
48     ct_twist_aux_mix,
49     ct_pitch_octave,
50     ct_pitch_semi7bp,
51     ct_pitch_semi7bp_absolutable,
52     ct_pitch,
53     ct_fmratio,
54     ct_fmratio_int,
55     ct_pbdepth,
56     ct_syncpitch,
57     ct_amplitude,
58     ct_reverbshape,
59     ct_decibel,
60     ct_decibel_narrow,
61     ct_decibel_narrow_extendable,
62     ct_decibel_narrow_short_extendable, // shorter extend range
63     ct_decibel_extra_narrow,
64     ct_decibel_attenuation,
65     ct_decibel_attenuation_clipper,
66     ct_decibel_attenuation_large,
67     ct_decibel_fmdepth,
68     ct_decibel_extendable,
69     ct_decibel_deactivatable,
70     ct_freq_audible,
71     ct_freq_audible_deactivatable,
72     ct_freq_audible_with_tunability, // we abuse 'extended' to mean 'use SCL tunign'
73     ct_freq_audible_with_very_low_lowerbound,
74     ct_freq_mod,
75     ct_freq_hpf,
76     ct_freq_shift,
77     ct_freq_vocoder_low,
78     ct_freq_vocoder_high,
79     ct_bandwidth,
80     ct_envtime,
81     ct_envtime_lfodecay,
82     ct_envshape,
83     ct_envshape_attack,
84     ct_envmode,
85     ct_delaymodtime,
86     ct_reverbtime,
87     ct_reverbpredelaytime,
88     ct_portatime,
89     ct_lforate,
90     ct_lforate_deactivatable,
91     ct_lfodeform,
92     ct_lfotype,
93     ct_lfotrigmode,
94     ct_detuning,
95     ct_osctype,
96     ct_fxtype,
97     ct_fxbypass,
98     ct_fbconfig,
99     ct_fmconfig,
100     ct_filtertype,
101     ct_filtersubtype,
102     ct_wstype,
103     ct_wt2window,
104     ct_osccount,
105     ct_osccountWT,
106     ct_oscspread,
107     ct_oscspread_bipolar,
108     ct_scenemode,
109     ct_scenesel,
110     ct_polymode,
111     ct_polylimit,
112     ct_midikey,
113     ct_midikey_or_channel,
114     ct_bool,
115     ct_bool_relative_switch,
116     ct_bool_link_switch,
117     ct_bool_keytrack,
118     ct_bool_retrigger,
119     ct_bool_unipolar,
120     ct_bool_mute,
121     ct_bool_solo,
122     ct_oscroute,
123     ct_stereowidth,
124     ct_bool_fm,
125     ct_character,
126     ct_sineoscmode,
127     ct_sinefmlegacy,
128     ct_countedset_percent, // what % through a counted set are you
129     ct_vocoder_bandcount,
130     ct_distortion_waveshape,
131     ct_flangerpitch,
132     ct_flangermode,
133     ct_fxlfowave,
134     ct_flangervoices,
135     ct_flangerspacing,
136     ct_osc_feedback,
137     ct_osc_feedback_negative,
138     ct_chorusmodtime,
139     ct_percent200,
140     ct_rotarydrive,
141     ct_sendlevel,
142     ct_phaser_stages,
143     ct_lfoamplitude,
144     ct_vocoder_modulator_mode,
145     ct_airwindows_fx,
146     ct_airwindows_param,
147     ct_airwindows_param_bipolar,
148     ct_airwindows_param_integral,
149     ct_amplitude_clipper,
150     ct_phaser_spread,
151     ct_decibel_narrow_deactivatable,
152     ct_decibel_extra_narrow_deactivatable,
153     ct_freq_reson_band1,
154     ct_freq_reson_band2,
155     ct_freq_reson_band3,
156     ct_reson_mode,
157     ct_envtime_linkable_delay,
158     ct_reson_res_extendable,
159     ct_chow_ratio,
160     ct_nimbusmode,
161     ct_nimbusquality,
162     ct_pitch4oct,
163     ct_float_toggle,
164     ct_comp_attack_ms,
165     ct_comp_release_ms,
166     ct_freq_ringmod,
167     ct_modern_trimix,
168     ct_percent_oscdrift,
169     ct_stringosc_excitation_model,
170     ct_ensemble_lforate,
171     ct_twist_engine,
172     ct_ensemble_stages,
173     ct_ensemble_clockrate,
174     ct_alias_wave,
175     ct_alias_mask,
176     ct_alias_bits,
177     ct_tape_microns,
178     ct_tape_speed,
179     num_ctrltypes,
180 };
181 
182 enum ControlStyle
183 {
184     cs_off = 0,
185 };
186 
187 enum ControlGroup
188 {
189     cg_GLOBAL = 0,
190     cg_OSC = 2,
191     cg_MIX = 3,
192     cg_FILTER = 4,
193     cg_ENV = 5,
194     cg_LFO = 6,
195     cg_FX = 7,
196     endCG
197 };
198 
199 struct ParamUserData
200 {
201     virtual ~ParamUserData() = default;
202 };
203 
204 struct CountedSetUserData : public ParamUserData
205 {
206     virtual int getCountedSetSize() = 0; // A constant time answer to the count of the set
207 };
208 
209 struct ParameterExternalFormatter : public ParamUserData
210 {
211     virtual void formatValue(float value, char *txt, int txtlen) = 0;
212     virtual bool stringToValue(const char *txt, float &outVal) = 0;
213 };
214 
215 struct ParameterDiscreteIndexRemapper : public ParamUserData
216 {
217     virtual int remapStreamedIndexToDisplayIndex(int i) = 0;
218     virtual std::string nameAtStreamedIndex(int i) = 0;
hasGroupNamesParameterDiscreteIndexRemapper219     virtual bool hasGroupNames() { return false; }
groupNameAtStreamedIndexParameterDiscreteIndexRemapper220     virtual std::string groupNameAtStreamedIndex(int i) { return ""; } // If you want menu grouping
sortGroupNamesParameterDiscreteIndexRemapper221     virtual bool sortGroupNames() { return true; }
useRemappedOrderingForGroupsIfNotSortedParameterDiscreteIndexRemapper222     virtual bool useRemappedOrderingForGroupsIfNotSorted() { return false; }
223 
supportsTotalIndexOrderingParameterDiscreteIndexRemapper224     virtual bool supportsTotalIndexOrdering() { return false; }
totalIndexOrderingParameterDiscreteIndexRemapper225     virtual const std::vector<int> totalIndexOrdering() { return std::vector<int>(); }
226 };
227 
228 class Parameter;
229 struct ParameterDynamicNameFunction
230 {
231     virtual const char *getName(Parameter *p) = 0;
232 };
233 /*
234  * The DBF binds to a couple of properties (deactivate, bipolar) so have a general
235  * base class
236  */
237 struct ParameterDynamicBoolFunction
238 {
239     virtual const bool getValue(Parameter *p) = 0;
240 };
241 
242 struct ParameterDynamicDeactivationFunction : public ParameterDynamicBoolFunction
243 {
getPrimaryDeactivationDriverParameterDynamicDeactivationFunction244     virtual Parameter *getPrimaryDeactivationDriver(Parameter *p) { return nullptr; };
245 };
246 
247 /*
248 ** It used to be parameters were assigned IDs in SurgePatch using an int which we ++ed along the
249 ** way. If we want to 'add at the end' but 'cluster together' we need a different data strcture
250 ** to allow clusters of parameters, so instead make SurgePatch use a linked list (or multiple lists)
251 ** while constructing params and then resolve them all at the end. This little class lets us
252 ** do that.
253 */
254 struct ParameterIDCounter
255 {
256     struct ParameterIDPromise
257     {
258         std::shared_ptr<ParameterIDPromise> next;
ParameterIDPromiseParameterIDCounter::ParameterIDPromise259         ParameterIDPromise() : next(nullptr) {}
260         ~ParameterIDPromise() = default;
261         long value = -1;
262     };
263 
ParameterIDCounterParameterIDCounter264     ParameterIDCounter()
265     {
266         head = std::make_shared<ParameterIDPromise>();
267         tail = head;
268     }
~ParameterIDCounterParameterIDCounter269     ~ParameterIDCounter()
270     {
271         head = nullptr;
272         tail = nullptr;
273     }
274 
275     typedef std::shared_ptr<ParameterIDPromise> promise_t;
276     typedef ParameterIDPromise
277         *promise_ptr_t; // use this for constant size carefully managed weak references
278 
279     promise_t head, tail;
280 
281     // This is a post-increment operator
nextParameterIDCounter282     promise_t next()
283     {
284         promise_t n(new ParameterIDPromise());
285         tail->next = n;
286         auto ret = tail;
287         tail = n;
288         return ret;
289     };
290 
resolveParameterIDCounter291     void resolve() const
292     {
293         auto h = head;
294         int val = 0;
295         while (h.get())
296         {
297             h->value = val++;
298             h = h->next;
299         }
300     }
301 };
302 
303 // used to make the infowindow
304 struct ModulationDisplayInfoWindowStrings
305 {
306     std::string val;
307     std::string valplus;
308     std::string valminus;
309     std::string dvalplus;
310     std::string dvalminus;
311 };
312 
313 class SurgeStorage;
314 
315 /*
316 ** WARNING WARNING
317 **
318 ** Parameter is copied with memcpy
319 ** Don't have complex types as members therefore
320 */
321 class Parameter
322 {
323   public:
324     Parameter();
325 
326   private:
327     Parameter *assign(ParameterIDCounter::promise_t id, int pid, const char *name,
328                       const char *dispname, int ctrltype,
329 
330                       std::string ui_identifier, int posx, int posy,
331 
332                       int scene = 0, ControlGroup ctrlgroup = cg_GLOBAL, int ctrlgroup_entry = 0,
333                       bool modulateable = true, int ctrlstyle = cs_off,
334                       bool defaultDeactivation = true);
335 
336   public:
337     Parameter *assign(ParameterIDCounter::promise_t id, int pid, const char *name,
338                       const char *dispname, int ctrltype,
339 
340                       const Surge::Skin::Connector &c,
341 
342                       int scene = 0, ControlGroup ctrlgroup = cg_GLOBAL, int ctrlgroup_entry = 0,
343                       bool modulateable = true, int ctrlstyle = cs_off,
344                       bool defaultDeactivation = true);
345     virtual ~Parameter();
346 
347     bool can_temposync();
348     bool can_extend_range();
349     bool can_be_absolute();
350     bool can_deactivate();
351     bool can_setvalue_from_string();
352     void clear_flags();
353     bool has_portaoptions();
354     bool has_deformoptions();
355     bool is_bipolar();
356     bool is_discrete_selection(); // basically a hint to use a dropdown not a slider
357     bool is_nonlocal_on_change(); // basically a change to me means other vals change so redraw
358                                   // everyone else too
359 
360     /*
361      * Why "appears deactivated" vs "is_deactivated". Well we have primary items
362      * which are deactivated. That's cool. But sometimes a value change on another
363      * parameter or a deactivation of another control means that a subordinate control
364      * should also present as deactivated. So this is an API that clients of the parameter
365      * can use to determine whether the system intends this parameter to be editable
366      * by user action. DSP branches and stuff should still just use primary.deactivated
367      *
368      * For UI things, we will want to find the driver of deactivation (if there is
369      * one) so we can share the activate menu. get_primary_deactivation_driver does that
370      * basically meaning that the menu on B can toggle the decativated state on A by
371      * having B be able to locate A (A being the 'primary deactivation driver')
372      */
373     bool appears_deactivated();
374     Parameter *get_primary_deactivation_driver();
375 
376     void set_type(int ctrltype);
377     void morph(Parameter *a, Parameter *b, float x);
378     //	void morph(parameter *b, float x);
379     pdata morph(Parameter *b, float x);
380     const char *get_name();
381     const char *get_full_name();
382     void set_name(const char *n); // never change name_storage as it is used for storage/recall
383     const char *get_internal_name();
384     const char *get_storage_name();
385     const wchar_t *getUnit() const;
386     void get_display(char *txt, bool external = false, float ef = 0.f);
387     enum ModulationDisplayMode
388     {
389         TypeIn,
390         Menu,
391         InfoWindow
392     };
393 
394     void get_display_of_modulation_depth(char *txt, float modulationDepth, bool isBipolar,
395                                          ModulationDisplayMode mode,
396                                          ModulationDisplayInfoWindowStrings *iw = nullptr);
397     void get_display_alt(char *txt, bool external = false, float ef = 0.f);
398     char *get_storage_value(char *);
399     void set_storage_value(int i);
400     void set_storage_value(float f);
401     float get_extended(float f);
402     float get_value_f01();
403     float normalized_to_value(float value);
404     float value_to_normalized(float value);
405     float get_default_value_f01();
406     void set_value_f01(float v, bool force_integer = false);
407     bool set_value_from_string(std::string s);
408     bool set_value_from_string_onto(std::string s, pdata &ontoThis);
409     float
410     get_modulation_f01(float mod); // used by the gui to get the position of the modulated handle
411     float set_modulation_f01(float v); // used by the gui to set the modulation to match the
412                                        // position of the modulated handle
413     float calculate_modulation_value_from_string(const std::string &s, bool &valid);
414 
415     void bound_value(bool force_integer = false);
416     std::string tempoSyncNotationValue(float f);
417     float quantize_modulation(float modvalue); // given a mod-value hand it back rounded to a
418                                                // 'reasonable' step size (used in ctrl-drag)
419 
420     void create_fullname(const char *dn, char *fn, ControlGroup ctrlgroup, int ctrlgroup_entry,
421                          const char *lfoPrefixOverride = nullptr);
422 
423     pdata val{}, val_default{}, val_min{}, val_max{};
424     // You might be tempted to use a non-fixed-size member here, like a std::string, but
425     // this class gets pre-c++ memcopied so thats not an option which is why I do this wonky
426     // pointer thing and strncpy from a string onto ui_identifier
427     ParameterIDCounter::promise_ptr_t id_promise{};
428     int id{};
429     char name[NAMECHARS]{}, dispname[NAMECHARS]{}, name_storage[NAMECHARS]{}, fullname[NAMECHARS]{};
430     char ui_identifier[NAMECHARS]{};
431     bool modulateable{};
432     int valtype = 0;
433     int scene{}; // 0 = patch, 1 = scene A, 2 = scene B
434     int ctrltype{};
435     int posx, posy, posy_offset;
436     ControlGroup ctrlgroup = cg_GLOBAL;
437     int ctrlgroup_entry = 0;
438     int ctrlstyle = cs_off;
439     int midictrl{};
440     int param_id_in_scene{};
441     bool affect_other_parameters{};
442     float moverate{};
443     bool per_voice_processing{};
444     bool temposync{}, extend_range{}, absolute{}, deactivated{};
445     bool porta_constrate{}, porta_gliss{}, porta_retrigger{};
446     int porta_curve{};
447     int deform_type{};
448 
449     enum ParamDisplayType
450     {
451         Custom,
452         LinearWithScale,
453         ATwoToTheBx,
454         Decibel,
455         DelegatedToFormatter
456     } displayType = Custom;
457 
458     enum ParamDisplayFeatures
459     {
460         kHasCustomMinString = 1U << 0U,
461         kHasCustomMaxString = 1U << 1U,
462         kHasCustomDefaultString = 1U << 2U,
463         kHasCustomMinValue = 1U << 3U,
464         kHasCustomMaxValue = 1U << 4U,
465         kUnitsAreSemitonesOrKeys = 1U << 5U,
466         kScaleBasedOnIsBiPolar = 1U << 6U
467     };
468 
469 #define DISPLAYINFO_TXT_SIZE 128
470     struct DisplayInfo
471     {
472         char unit[DISPLAYINFO_TXT_SIZE]{}, absoluteUnit[DISPLAYINFO_TXT_SIZE]{};
473         float scale = 1;
474         float a = 1.0, b = 1.0;
475         int decimals = 2;
476         int64_t customFeatures = 0;
477 
478         float tempoSyncNotationMultiplier = 1.f;
479 
480         char minLabel[DISPLAYINFO_TXT_SIZE]{}, maxLabel[DISPLAYINFO_TXT_SIZE]{},
481             defLabel[DISPLAYINFO_TXT_SIZE]{};
482         float minLabelValue = 0.f, maxLabelValue = 0.f;
483 
484         float modulationCap = -1.f;
485 
486         bool supportsNoteName = false;
487 
488         float extendFactor = 1.0,
489               absoluteFactor = 1.0; // set these to 1 in case we sneak by and divide by accident
490     } displayInfo;
491 
492     ParamUserData *user_data = nullptr;    // I know this is a bit gross but we have a runtime type
493     void set_user_data(ParamUserData *ud); // I take a shallow copy and don't assume ownership and
494                                            // assume i am referencable
495 
496     bool supportsDynamicName();
497     ParameterDynamicNameFunction *dynamicName = nullptr;
498 
499     /*
500      * Handlers for dynamic deactivation and dynamic bipolarity
501      */
502     ParameterDynamicDeactivationFunction *dynamicDeactivation = nullptr;
503     ParameterDynamicBoolFunction *dynamicBipolar = nullptr;
504 
505     bool hasSkinConnector = false;
506 
507     /*
508     ** Parameter has a pointer to the storage that manages the patch that contains it
509     ** *if* this parameter was thus constructed. There are real and legitimate uses
510     ** of the Parameter class where this pointer will be null so if you use it you
511     ** have to check the nullity
512     **
513     ** if( storage && storage->isStandardTuning ) { }
514     */
515     SurgeStorage *storage = nullptr;
516 
517     /*
518      * These are definitions which surge has used since time immemorial. If you
519      * change them you will break saved automation states in projects, so we stuck
520      * with the somewhat odd 99 and 005 but put them all in one place and used the
521      * consistently.
522      */
523     static inline float intScaledToFloat(int v, int vmax, int vmin = 0)
524     {
525         return 0.005 + 0.99 * ((float)(v - vmin)) / ((float)(vmax - vmin));
526     }
527     static inline int intUnscaledFromFloat(float f, int vmax, int vmin = 0)
528     {
529         return (int)((1 / 0.99) * (f - 0.005) * (float)(vmax - vmin) + 0.5) + vmin;
530     }
531 };
532 
533 // I don't make this a member since param needs to be copyable with memcpy.
534 extern std::atomic<bool> parameterNameUpdated;
535