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