Sympathetic.cpp (12930B)
1 /* 2 ZynAddSubFX - a software synthesizer 3 4 Sympathetic.cpp - Distorsion effect 5 Copyright (C) 2002-2005 Nasca Octavian Paul 6 Author: Nasca Octavian Paul 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License 10 as published by the Free Software Foundation; either version 2 11 of the License, or (at your option) any later version. 12 */ 13 14 #include "Sympathetic.h" 15 #include "../DSP/AnalogFilter.h" 16 #include "CombFilterBank.h" 17 #include "../Misc/Allocator.h" 18 #include <cmath> 19 #include <rtosc/ports.h> 20 #include <rtosc/port-sugar.h> 21 #include "../globals.h" 22 23 namespace zyn { 24 25 #define rObject Sympathetic 26 #define rBegin [](const char *msg, rtosc::RtData &d) { 27 #define rEnd } 28 29 #define rEffParRange(name, idx, ...) \ 30 {STRINGIFY(name) "::i", rProp(parameter) rDefaultDepends(preset) \ 31 DOC(__VA_ARGS__), NULL, rEffParCb(idx)} 32 33 rtosc::Ports Sympathetic::ports = { 34 {"preset::i", rProp(parameter) 35 rOptions(Generic, Piano, Grand, Guitar, 12-String) 36 rProp(alias) 37 rDefault(0) 38 rDoc("Instrument Presets"), 0, 39 rBegin; 40 rObject *o = (rObject*)d.obj; 41 if(rtosc_narguments(msg)) 42 o->setpreset(rtosc_argument(msg, 0).i); 43 else 44 d.reply(d.loc, "i", o->Ppreset); 45 rEnd}, 46 {"presetOfVolume:", rProp(internal), 0, 47 rBegin; (void)msg; 48 rObject *o = (rObject*)d.obj; 49 d.reply(d.loc, "i", o->Ppreset + (16 * o->insertion)); 50 rEnd}, 51 rEffParVol(rDefaultDepends(presetOfVolume), 52 rPresetsAt(0, 84, 66, 53, 66, 60), 53 rPresetsAt(16, 127, 100, 80, 100, 90)), 54 rEffParPan(), 55 rEffPar(Pq, 2, rShort("q"), rDefault(65), 56 rPresets(125, 125, 125, 110, 110), "Resonance"), 57 rEffPar(Pdrive, 3, rShort("dr"), rDefault(65), 58 rPresets(5, 5, 5, 20, 20), "Input Amplification"), 59 rEffPar(Plevel, 4, rShort("lev"), rDefault(65), 60 rPresets(80, 80, 90, 65, 77), "Output Amplification"), 61 rEffPar(Punison_frequency_spread, 5, rShort("detune"), rDefault(30), 62 rPresets(10, 10, 5, 0, 10), "Unison String Detune"), 63 rEffParTF(Pnegate, 6, rShort("neg"), rDefault(false), "Negate Signal"), 64 rEffPar(Plpf, 7, rShort("lpf"), rDefault(127), "Low Pass Cutoff"), 65 rEffPar(Phpf, 8, rShort("hpf"), rDefault(0), "High Pass Cutoff"), 66 rEffParRange(Punison_size, 9, rShort("uni"), rLinear(1,3), rDefault(1), 67 rPresets(3, 3, 1, 1, 2), "Number of Unison Strings"), 68 rEffParRange(Pstrings, 10, rShort("str"), rLinear(0,76), 69 rPresets(12, 12, 60, 6, 6), "Number of Strings"), 70 rEffPar(Pbasenote, 11, rShort("base"), // basefreq = powf(2.0f, (basenote-69)/12)*440; 57->220Hz 71 rPresets(57, 57, 33, 52, 52), "Midi Note of Lowest String"), 72 rArrayF(freqs, 88, rLinear(27.50f,4186.01f), 73 "String Frequencies"), 74 }; 75 76 #undef rBegin 77 #undef rEnd 78 #undef rObject 79 80 Sympathetic::Sympathetic(EffectParams pars) 81 :Effect(pars), 82 Pvolume(127), 83 Pdrive(65), 84 Plevel(65), 85 Ptype(0), 86 Pnegate(0), 87 Plpf(127), 88 Phpf(0), 89 Pstereo(0), 90 Pq(65), 91 Punison_size(1), 92 Punison_frequency_spread(30), 93 Pstrings(12), 94 Pbasenote(57), 95 baseFreq(220.0f) 96 { 97 lpfl = memory.alloc<AnalogFilter>(2, 22000, 1, 0, pars.srate, pars.bufsize); 98 lpfr = memory.alloc<AnalogFilter>(2, 22000, 1, 0, pars.srate, pars.bufsize); 99 hpfl = memory.alloc<AnalogFilter>(3, 20, 1, 0, pars.srate, pars.bufsize); 100 hpfr = memory.alloc<AnalogFilter>(3, 20, 1, 0, pars.srate, pars.bufsize); 101 102 // precalc gainbwd_init = gainbwd_offset + gainbwd_factor * Pq 103 // 0.873f + 0.001f * 65 = 0.873f + 0.065f = 0.938f 104 filterBank = memory.alloc<CombFilterBank>(&memory, pars.srate, pars.bufsize, 0.938f); 105 106 setpreset(Ppreset); 107 calcFreqs(); // sets freqs 108 cleanup(); 109 } 110 111 Sympathetic::~Sympathetic() 112 { 113 memory.dealloc(lpfl); 114 memory.dealloc(lpfr); 115 memory.dealloc(hpfl); 116 memory.dealloc(hpfr); 117 memory.dealloc(filterBank); 118 } 119 120 //Cleanup the effect 121 void Sympathetic::cleanup(void) 122 { 123 lpfl->cleanup(); 124 hpfl->cleanup(); 125 lpfr->cleanup(); 126 hpfr->cleanup(); 127 } 128 129 130 //Apply the filters 131 void Sympathetic::applyfilters(float *efxoutl, float *efxoutr) 132 { 133 if(Plpf!=127) lpfl->filterout(efxoutl); 134 if(Phpf!=0) hpfl->filterout(efxoutl); 135 if(Pstereo != 0) { //stereo 136 if(Plpf!=127) lpfr->filterout(efxoutr); 137 if(Phpf!=0) hpfr->filterout(efxoutr); 138 } 139 } 140 141 142 //Effect output 143 void Sympathetic::out(const Stereo<float *> &smp) 144 { 145 float inputvol = powf(2.0f, (Pdrive - 65.0f) / 128.0f) / 2.0f; 146 if(Pnegate) 147 inputvol *= -1.0f; 148 149 if(Pstereo) //Stereo 150 for(int i = 0; i < buffersize; ++i) { 151 efxoutl[i] = smp.l[i] * inputvol * pangainL; 152 efxoutr[i] = smp.r[i] * inputvol * pangainR; 153 } 154 else //Mono 155 for(int i = 0; i < buffersize; ++i) 156 efxoutl[i] = (smp.l[i] * pangainL + smp.r[i] * pangainR) * inputvol; 157 158 filterBank->filterout(efxoutl); 159 if(Pstereo) 160 filterBank->filterout(efxoutr); 161 162 applyfilters(efxoutl, efxoutr); 163 164 if(!Pstereo) 165 memcpy(efxoutr, efxoutl, bufferbytes); 166 167 float level = dB2rap(60.0f * Plevel / 127.0f - 40.0f); 168 for(int i = 0; i < buffersize; ++i) { 169 float lout = efxoutl[i]; 170 float rout = efxoutr[i]; 171 float l = lout * (1.0f - lrcross) + rout * lrcross; 172 float r = rout * (1.0f - lrcross) + lout * lrcross; 173 lout = l; 174 rout = r; 175 176 efxoutl[i] = lout * 2.0f * level; 177 efxoutr[i] = rout * 2.0f * level; 178 } 179 } 180 181 //Parameter control 182 void Sympathetic::setvolume(unsigned char _Pvolume) 183 { 184 Pvolume = _Pvolume; 185 186 if(insertion == 0) { 187 outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f; 188 volume = 1.0f; 189 } 190 else 191 volume = outvolume = Pvolume / 127.0f; 192 if(Pvolume == 0) 193 cleanup(); 194 } 195 196 void Sympathetic::setlpf(unsigned char _Plpf) 197 { 198 Plpf = _Plpf; 199 float fr = expf(sqrtf(Plpf / 127.0f) * logf(25000.0f)) + 40.0f; 200 lpfl->setfreq(fr); 201 lpfr->setfreq(fr); 202 } 203 204 void Sympathetic::sethpf(unsigned char _Phpf) 205 { 206 Phpf = _Phpf; 207 float fr = expf(sqrtf(Phpf / 127.0f) * logf(25000.0f)) + 20.0f; 208 hpfl->setfreq(fr); 209 hpfr->setfreq(fr); 210 } 211 212 void Sympathetic::calcFreqs() 213 { 214 switch(Ppreset) { 215 case 0: 216 calcFreqsGeneric(); 217 break; 218 case 1: 219 case 2: 220 calcFreqsPiano(); 221 break; 222 case 3: 223 case 4: 224 calcFreqsGuitar(); 225 break; 226 } 227 } 228 229 void Sympathetic::calcFreqsGeneric() 230 { 231 const float unison_spread_semicent = powf(Punison_frequency_spread / 63.5f, 2.0f) * 25.0f; 232 const float unison_real_spread_up = powf(2.0f, (unison_spread_semicent * 0.5f) / 1200.0f); 233 const float unison_real_spread_down = 1.0f/unison_real_spread_up; 234 235 for(unsigned int i = 0; i < Punison_size*Pstrings; i+=Punison_size) 236 { 237 const float centerFreq = powf(2.0f, (float)i / 36.0f) * baseFreq; 238 filterBank->delays[i] = ((float)samplerate)/centerFreq; 239 if (Punison_size > 1) filterBank->delays[i+1] = ((float)samplerate)/(centerFreq * unison_real_spread_up); 240 if (Punison_size > 2) filterBank->delays[i+2] = ((float)samplerate)/(centerFreq * unison_real_spread_down); 241 } 242 filterBank->setStrings(Pstrings*Punison_size,baseFreq); 243 244 } 245 246 void Sympathetic::calcFreqsPiano() 247 { 248 const float unison_spread_semicent = powf(Punison_frequency_spread / 63.5f, 2.0f) * 25.0f; 249 const float unison_real_spread_up = powf(2.0f, (unison_spread_semicent * 0.5f) / 1200.0f); 250 const float unison_real_spread_down = 1.0f/unison_real_spread_up; 251 252 for(unsigned int i = 0; i < Punison_size*Pstrings; i+=Punison_size) 253 { 254 const float centerFreq = powf(2.0f, (float)i / 36.0f) * baseFreq; 255 const unsigned int stringchoir_size = 256 i>num_single_strings ? (i>Pstrings-num_triple_strings ? 3 : 2) :1; 257 filterBank->delays[i] = ((float)samplerate)/centerFreq; 258 if (stringchoir_size > 1) filterBank->delays[i+1] = ((float)samplerate)/(centerFreq * unison_real_spread_up); 259 else filterBank->delays[i+1] = 0; 260 if (stringchoir_size > 2) filterBank->delays[i+2] = ((float)samplerate)/(centerFreq * unison_real_spread_down); 261 else filterBank->delays[i+2] = 0; 262 } 263 filterBank->setStrings(Pstrings*Punison_size,baseFreq); 264 265 } 266 267 void Sympathetic::calcFreqsGuitar() 268 { 269 const float unison_spread_semicent = powf(Punison_frequency_spread / 63.5f, 2.0f) * 25.0f; 270 const float unison_real_spread_up = powf(2.0f, (unison_spread_semicent * 0.5f) / 1200.0f); 271 272 for(auto i = 0; i < 6*Punison_size; i+=Punison_size) 273 { 274 assert(guitar_freqs[i/Punison_size]>0.0f); 275 filterBank->delays[i] = ((float)samplerate)/guitar_freqs[i/Punison_size]; 276 if (Punison_size > 1) filterBank->delays[i+1] = ((float)samplerate)/(guitar_freqs[i/Punison_size] * unison_real_spread_up); 277 } 278 filterBank->setStrings(Pstrings*Punison_size,guitar_freqs[0]); 279 280 } 281 282 unsigned char Sympathetic::getpresetpar(unsigned char npreset, unsigned int npar) 283 { 284 #define PRESET_SIZE 13 285 #define NUM_PRESETS 5 286 static const unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { 287 //Vol Pan Q Drive Lev Spr neg lp hp sz strings note 288 //Generic 289 {127, 64, 125, 5, 80, 10, 0, 127, 0, 3, 12, 57}, 290 //Piano 12-String 291 {100, 64, 125, 5, 80, 10, 0, 127, 0, 3, 12, 57}, 292 //Piano 60-String 293 {80, 64, 125, 5, 90, 5, 0, 127, 0, 1, 60, 33}, 294 //Guitar 6-String 295 {100, 64, 110, 20, 65, 0, 0, 127, 0, 1, 6, 52}, 296 //Guitar 12-String 297 {90, 64, 110, 20, 77, 10, 0, 127, 0, 2, 6, 52}, 298 }; 299 if(npreset < NUM_PRESETS && npar < PRESET_SIZE) { 300 if(npar == 0 && insertion == 0) { 301 /* lower the volume if this is system effect */ 302 return (2 * presets[npreset][npar]) / 3; 303 } 304 return presets[npreset][npar]; 305 } 306 return 0; 307 } 308 309 void Sympathetic::setpreset(unsigned char npreset) 310 { 311 if(npreset >= NUM_PRESETS) 312 npreset = NUM_PRESETS - 1; 313 for(int n = 0; n != 128; n++) 314 changepar(n, getpresetpar(npreset, n)); 315 Ppreset = npreset; 316 317 calcFreqs(); 318 cleanup(); 319 } 320 321 322 void Sympathetic::changepar(int npar, unsigned char value) 323 { 324 switch(npar) { 325 case 0: 326 setvolume(value); 327 break; 328 case 1: 329 setpanning(value); 330 break; 331 case 2: 332 Pq = value; 333 filterBank->gainbwd = gainbwd_offset + (float)Pq * gainbwd_factor; 334 break; 335 case 3: 336 Pdrive = value; 337 filterBank->inputgain = (float)Pdrive/65.0f; 338 break; 339 case 4: 340 Plevel = value; 341 filterBank->outgain = (float)Plevel/65.0f; 342 break; 343 case 5: 344 if(Punison_frequency_spread != value) 345 { 346 Punison_frequency_spread = value; 347 calcFreqs(); 348 } 349 break; 350 case 6: 351 if(value > 1) 352 Pnegate = 1; 353 else 354 Pnegate = value; 355 break; 356 case 7: 357 setlpf(value); 358 break; 359 case 8: 360 sethpf(value); 361 break; 362 case 9: 363 { 364 auto lim = limit(value, (unsigned char) 1, (unsigned char) 3); 365 if(Punison_size != lim) 366 { 367 Punison_size = lim; 368 if (Punison_size>2) Ppreset=0; 369 calcFreqs(); 370 } 371 break; 372 } 373 case 10: 374 { 375 auto lim = limit(value, (unsigned char) 0, (unsigned char) 76); 376 if(Pstrings != lim) 377 { 378 Pstrings = lim; 379 if (Pstrings>6) Ppreset=0; 380 calcFreqs(); 381 } 382 break; 383 } 384 case 11: 385 if (Pbasenote != value) 386 { 387 Pbasenote = value; 388 baseFreq = powf(2.0f, ((float)Pbasenote-69.0f)/12.0f)*440.0f; 389 calcFreqs(); 390 } 391 break; 392 default: 393 break; 394 } 395 } 396 397 unsigned char Sympathetic::getpar(int npar) const 398 { 399 switch(npar) { 400 case 0: return Pvolume; 401 case 1: return Ppanning; 402 case 2: return Pq; 403 case 3: return Pdrive; 404 case 4: return Plevel; 405 case 5: return Punison_frequency_spread; 406 case 6: return Pnegate; 407 case 7: return Plpf; 408 case 8: return Phpf; 409 case 9: return Punison_size; 410 case 10: return Pstrings; 411 case 11: return Pbasenote; 412 default: return 0; //in case of bogus parameter number 413 } 414 } 415 416 }