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 #include "Parameter.h" 17 #pragma once 18 19 /* 20 * OK so what the heck is happening here, you may ask? Well, let me explain. Grab a cup of tea. 21 * 22 * As we approached Surge 1.8 with new filters, we realized our filter list was getting 23 * long. So we decided to use submenus. But when doing that, we realized that the 24 * natural grouping of filters (LP, BP, HP, Notch, Special...) didn't work with the way 25 * our filter models had worked. We had things like "bandpass doesn't split 12 dB/24 dB" into 26 * separate types, and "OB-Xd is kinda all sorts of filters". So, over in issue #3006, 27 * we decided that some filters needed splitting, kinda like what Luna had done in the non-linear 28 * feedback set. But splitting means that subtype counts change and streaming breaks. 29 * And we wanted to split filters which weren't in streaming version 14 (1.7->1.8) either. 30 * 31 * So what we did was add in streaming version 15 a "post patch streaming fixup" operation 32 * which allows you to see the prior version, the current version, and adjust. That way we can 33 * do things like "OB-Xd subtype 7 in streaming version 14 is actually OB-Xd highpass subtype 3 34 * in streaming version 15 and above". Or whatever. 35 * 36 * But to do *that*, we need to keep the old enums around so we can write that code. So these 37 * are the old enums. 38 * 39 * Then the only question left is - how to split? I chose the 'add at end for splits' method. That 40 * is, fut_14_bp12 splits into fut_bp12 and fut_bp24, but I added fut_bp24 at the end of the list. 41 * Pros and cons: if I added it adjacent, the names in the name array would line up, but the 42 * remapping code would be wildly more complicated. I chose simple remapping code (that is S&H and 43 * vintage ladder are no-ops in remap) at the cost of an oddly ordered filter name list. That's the 44 * right choice, but when you curse me for the odd name list, you can come back and read this 45 * comment and feel slightly better. Finally, items which split and changed meaning got a new name 46 * (so fut_comb is now fut_comp_pos and fut_comb_neg, say), which requires us to go and fix up any 47 * code which refered to the old values. 48 */ 49 50 enum fu_type_sv14 51 { 52 fut_14_none = 0, 53 fut_14_lp12, 54 fut_14_lp24, 55 fut_14_lpmoog, 56 fut_14_hp12, 57 fut_14_hp24, 58 fut_14_bp12, 59 fut_14_notch12, 60 fut_14_comb, 61 fut_14_SNH, 62 fut_14_vintageladder, 63 fut_14_obxd_2pole, 64 fut_14_obxd_4pole, 65 fut_14_k35_lp, 66 fut_14_k35_hp, 67 fut_14_diode, 68 fut_14_cutoffwarp_lp, 69 fut_14_cutoffwarp_hp, 70 fut_14_cutoffwarp_n, 71 fut_14_cutoffwarp_bp, 72 n_fu_14_types, 73 }; 74 75 enum fu_type 76 { 77 fut_none = 0, 78 fut_lp12, 79 fut_lp24, 80 fut_lpmoog, 81 fut_hp12, 82 fut_hp24, 83 fut_bp12, // ADJ 84 fut_notch12, // ADH 85 fut_comb_pos, 86 fut_SNH, 87 fut_vintageladder, 88 fut_obxd_2pole_lp, // ADJ 89 fut_obxd_4pole, 90 fut_k35_lp, 91 fut_k35_hp, 92 fut_diode, 93 fut_cutoffwarp_lp, 94 fut_cutoffwarp_hp, 95 fut_cutoffwarp_n, 96 fut_cutoffwarp_bp, 97 fut_obxd_2pole_hp, 98 fut_obxd_2pole_n, 99 fut_obxd_2pole_bp, 100 fut_bp24, 101 fut_notch24, 102 fut_comb_neg, 103 fut_apf, 104 fut_cutoffwarp_ap, 105 fut_resonancewarp_lp, 106 fut_resonancewarp_hp, 107 fut_resonancewarp_n, 108 fut_resonancewarp_bp, 109 fut_resonancewarp_ap, 110 n_fu_types, 111 }; 112 113 /* 114 * Each filter needs w names (alas). there's the name we show in the automation parameter and 115 * so on (the value for get_display_name) which is in 'fut_names'. There's the value we put 116 * in the menu which generally strips out Lowpass and Highpass and stuff, since they are already 117 * grouped in submenus, and this is in fut_menu array 118 */ 119 const char fut_names[n_fu_types][32] = { 120 "Off", // fut_none 121 "LP 12 dB", // fut_lp12 122 "LP 24 dB", // fut_lp24 123 "LP Legacy Ladder", // fut_lpmoog 124 "HP 12 dB", // fut_hp12 125 "HP 24 dB", // fut_hp24 126 "BP 12 dB", // fut_bp12 127 "N 12 dB", // fut_notch12 128 "FX Comb +", // fut_comb_pos 129 "FX Sample & Hold", // fut_SNH 130 "LP Vintage Ladder", // fut_vintageladder 131 "LP OB-Xd 12 dB", // fut_obxd_2pole_lp 132 "LP OB-Xd 24 dB", // fut_obxd_4pole 133 "LP K35", // fut_k35_lp 134 "HP K35", // fut_k35_hp 135 "LP Diode Ladder", // fut_diode 136 "LP Cutoff Warp", // fut_cutoffwarp_lp 137 "HP Cutoff Warp", // fut_cutoffwarp_hp 138 "N Cutoff Warp", // fut_cutoffwarp_n 139 "BP Cutoff Warp", // fut_cutoffwarp_bp 140 "HP OB-Xd 12 dB", // fut_obxd_2pole_hp 141 "N OB-Xd 12 dB", // fut_obxd_2pole_n 142 "BP OB-Xd 12 dB", // fut_obxd_2pole_bp 143 "BP 24 dB", // fut_bp24 144 "N 24 dB", // fut_notch24 145 "FX Comb -", // fut_comb_neg 146 "FX Allpass", // fut_apf 147 "FX Cutoff Warp AP", // fut_cutoffwarp_ap 148 "LP Res Warp", // fut_resonancewarp_lp 149 "HP Res Warp", // fut_resonancewarp_hp 150 "N Res Warp", // fut_resonancewarp_n 151 "BP Res Warp", // fut_resonancewarp_bp 152 "FX Res Warp AP", // fut_resonancewarp_ap 153 /* this is a ruler to ensure names do not exceed 31 characters 154 0123456789012345678901234567890 155 */ 156 }; 157 158 const char fut_menu_names[n_fu_types][32] = { 159 "Off", 160 "12 dB", // LP 161 "24 dB", // LP 162 "Legacy Ladder", 163 "12 dB", // HP 164 "24 dB", // HP 165 "12 dB", // BP 166 "12 dB", // N 167 "Comb +", 168 "Sample & Hold", 169 "Vintage Ladder", 170 "OB-Xd 12 dB", // LP 171 "OB-Xd 24 dB", // LP 172 "K35", // LP 173 "K35", // HP 174 "Diode Ladder", 175 "Cutoff Warp", // LP 176 "Cutoff Warp", // HP 177 "Cutoff Warp", // N 178 "Cutoff Warp", // BP 179 "OB-Xd 12 dB", // HP 180 "OB-Xd 12 dB", // N 181 "OB-Xd 12 dB", // BP 182 "24 dB", // BP 183 "24 dB", // N 184 "Comb -", 185 "Allpass", 186 "Cutoff Warp Allpass", 187 "Resonance Warp", // LP 188 "Resonance Warp", // HP 189 "Resonance Warp", // N 190 "Resonance Warp", // BP 191 "Resonance Warp Allpass", 192 /* this is a ruler to ensure names do not exceed 31 characters 193 0123456789012345678901234567890 194 */ 195 }; 196 197 const char fut_bp_subtypes[3][32] = { 198 "Clean", 199 "Driven", 200 "Smooth", 201 }; 202 203 const char fut_notch_subtypes[2][32] = { 204 "Standard", 205 "Mild", 206 }; 207 208 const char fut_comb_subtypes[2][64] = { 209 "50% Wet", 210 "100% Wet", 211 }; 212 213 const char fut_def_subtypes[3][32] = { 214 "Clean", 215 "Driven", 216 "Smooth", 217 }; 218 219 const char fut_ldr_subtypes[4][32] = { 220 "6 dB", 221 "12 dB", 222 "18 dB", 223 "24 dB", 224 }; 225 226 const char fut_vintageladder_subtypes[6][32] = { 227 "Type 1", 228 "Type 1 Compensated", 229 "Type 2", 230 "Type 2 Compensated", 231 }; 232 233 const char fut_obxd_2p_subtypes[2][32] = {"Standard", "Pushed"}; 234 235 const char fut_obxd_4p_subtypes[4][32] = { 236 "6 dB", 237 "12 dB", 238 "18 dB", 239 "24 dB", 240 }; 241 242 const char fut_k35_subtypes[5][32] = {"No Saturation", "Mild Saturation", "Moderate Saturation", 243 "Heavy Saturation", "Extreme Saturation"}; 244 245 const float fut_k35_saturations[5] = {0.0f, 1.0f, 2.0f, 3.0f, 4.0f}; 246 247 const char fut_nlf_subtypes[4][32] = { 248 "1 Stage", 249 "2 Stages", 250 "3 Stages", 251 "4 Stages", 252 }; 253 254 const char fut_nlf_saturators[4][16] = { 255 "tanh", 256 "Soft Clip", 257 "OJD", 258 "Sine", 259 }; 260 261 const int fut_subcount[n_fu_types] = { 262 0, // fut_none 263 3, // fut_lp12 264 3, // fut_lp24 265 4, // fut_lpmoog 266 3, // fut_hp12 267 3, // fut_hp24 268 3, // fut_bp12 269 2, // fut_notch12 270 2, // fut_comb_pos 271 0, // fut_SNH 272 4, // fut_vintageladder 273 2, // fut_obxd_2pole 274 4, // fut_obxd_4pole 275 5, // fut_k35_lp 276 5, // fut_k35_hp 277 4, // fut_diode 278 12, // fut_cutoffwarp_lp 279 12, // fut_cutoffwarp_hp 280 12, // fut_cutoffwarp_n 281 12, // fut_cutoffwarp_bp 282 2, // fut_obxd_2pole_hp, 283 2, // fut_obxd_2pole_n, 284 2, // fut_obxd_2pole_bp, 285 3, // fut_bp24, 286 2, // fut_notch24, 287 2, // fut_comb_neg, 288 0, // fut_apf 289 12, // fut_cutoffwarp_ap 290 8, // fut_resonancewarp_lp 291 8, // fut_resonancewarp_hp 292 8, // fut_resonancewarp_n 293 8, // fut_resonancewarp_bp 294 8, // fut_resonancewarp_ap 295 }; 296 297 enum fu_subtype 298 { 299 st_SVF = 0, 300 st_Rough = 1, 301 st_Smooth = 2, 302 st_Medium = 3, // disabled 303 st_Notch = 0, 304 st_NotchMild = 1, 305 }; 306 307 struct FilterSelectorMapper : public ParameterDiscreteIndexRemapper 308 { 309 std::vector<std::pair<int, std::string>> mapping; 310 std::unordered_map<int, int> inverseMapping; pFilterSelectorMapper311 void p(int i, std::string s) { mapping.push_back(std::make_pair(i, s)); } FilterSelectorMapperFilterSelectorMapper312 FilterSelectorMapper() 313 { 314 p(fut_none, ""); 315 316 p(fut_lp12, "Lowpass"); 317 p(fut_lp24, "Lowpass"); 318 p(fut_lpmoog, "Lowpass"); 319 p(fut_vintageladder, "Lowpass"); 320 p(fut_k35_lp, "Lowpass"); 321 p(fut_diode, "Lowpass"); 322 p(fut_obxd_2pole_lp, "Lowpass"); // ADJ 323 p(fut_obxd_4pole, "Lowpass"); 324 p(fut_cutoffwarp_lp, "Lowpass"); 325 p(fut_resonancewarp_lp, "Lowpass"); 326 327 p(fut_bp12, "Bandpass"); 328 p(fut_bp24, "Bandpass"); 329 p(fut_obxd_2pole_bp, "Bandpass"); 330 p(fut_cutoffwarp_bp, "Bandpass"); 331 p(fut_resonancewarp_bp, "Bandpass"); 332 333 p(fut_hp12, "Highpass"); 334 p(fut_hp24, "Highpass"); 335 p(fut_k35_hp, "Highpass"); 336 p(fut_obxd_2pole_hp, "Highpass"); 337 p(fut_cutoffwarp_hp, "Highpass"); 338 p(fut_resonancewarp_hp, "Highpass"); 339 340 p(fut_notch12, "Notch"); 341 p(fut_notch24, "Notch"); 342 p(fut_obxd_2pole_n, "Notch"); 343 p(fut_cutoffwarp_n, "Notch"); 344 p(fut_resonancewarp_n, "Notch"); 345 346 p(fut_apf, "Effect"); 347 p(fut_cutoffwarp_ap, "Effect"); 348 p(fut_resonancewarp_ap, "Effect"); 349 p(fut_comb_pos, "Effect"); 350 p(fut_comb_neg, "Effect"); 351 p(fut_SNH, "Effect"); 352 353 int c = 0; 354 for (auto e : mapping) 355 inverseMapping[e.first] = c++; 356 357 if (mapping.size() != n_fu_types) 358 std::cout << "BAD MAPPING TYPES" << std::endl; 359 } 360 remapStreamedIndexToDisplayIndexFilterSelectorMapper361 virtual int remapStreamedIndexToDisplayIndex(int i) override { return inverseMapping[i]; } nameAtStreamedIndexFilterSelectorMapper362 virtual std::string nameAtStreamedIndex(int i) override { return fut_menu_names[i]; } hasGroupNamesFilterSelectorMapper363 virtual bool hasGroupNames() override { return true; }; 364 groupNameAtStreamedIndexFilterSelectorMapper365 virtual std::string groupNameAtStreamedIndex(int i) override 366 { 367 return mapping[inverseMapping[i]].second; 368 } 369 sortGroupNamesFilterSelectorMapper370 virtual bool sortGroupNames() override { return false; } useRemappedOrderingForGroupsIfNotSortedFilterSelectorMapper371 bool useRemappedOrderingForGroupsIfNotSorted() override { return true; } 372 supportsTotalIndexOrderingFilterSelectorMapper373 virtual bool supportsTotalIndexOrdering() override { return true; } totalIndexOrderingFilterSelectorMapper374 virtual const std::vector<int> totalIndexOrdering() override 375 { 376 auto res = std::vector<int>(); 377 for (auto m : mapping) 378 res.push_back(m.first); 379 return res; 380 } 381 }; 382 383 /* 384 * Finally we need to map streaming indices to positions on the glyph display. This 385 * should *really* be in UI code but it is just a declaration and having all the declarations 386 * together is useful. In the far distant future perhaps we customize this by skin. 387 */ 388 389 const int lprow = 1; 390 const int bprow = 2; 391 const int hprow = 3; 392 const int nrow = 4; 393 const int fxrow = 5; 394 const int fut_glyph_index[n_fu_types][2] = { 395 {0, 0}, // fut_none 396 {0, lprow}, // fut_lp12 397 {1, lprow}, // fut_lp24 398 {3, lprow}, // fut_lpmoog 399 {0, hprow}, // fut_hp12 400 {1, hprow}, // fut_hp24 401 {0, bprow}, // fut_bp12 402 {0, nrow}, // fut_notch12 403 {1, fxrow}, // fut_comb_pos 404 {3, fxrow}, // fut_SNH 405 {4, lprow}, // fut_vintageladder 406 {6, lprow}, // fut_obxd_2pole 407 {7, lprow}, // fut_obxd_4pole 408 {2, lprow}, // fut_k35_lp 409 {2, hprow}, // fut_k35_hp 410 {5, lprow}, // fut_diode 411 {8, lprow}, // fut_cutoffwarp_lp 412 {4, hprow}, // fut_cutoffwarp_hp 413 {3, nrow}, // fut_cutoffwarp_n 414 {3, bprow}, // fut_cutoffwarp_bp 415 {3, hprow}, // fut_obxd_2pole_hp, 416 {2, nrow}, // fut_obxd_2pole_n, 417 {2, bprow}, // fut_obxd_2pole_bp, 418 {1, bprow}, // fut_bp24, 419 {1, nrow}, // fut_notch24, 420 {2, fxrow}, // fut_comb_neg, 421 {0, fxrow}, // fut_apf 422 {0, fxrow}, // fut_cutoffwarp_ap (this is temporarily set to just use the regular AP glyph) 423 {9, lprow}, // fut_resonancewarp_lp 424 {5, hprow}, // fut_resonancewarp_hp 425 {4, nrow}, // fut_resonancewarp_n 426 {4, bprow}, // fut_resonancewarp_bp 427 {0, fxrow}, // fut_resonancewarp_ap (also temporarily set to just use the regular AP glyph) 428 }; 429