/* ZynAddSubFX - a software synthesizer Alienwah.cpp - "AlienWah" effect Copyright (C) 2002-2005 Nasca Octavian Paul Author: Nasca Octavian Paul This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include "../Misc/Allocator.h" #include "Alienwah.h" namespace zyncarla { using std::complex; #define rObject Alienwah #define rBegin [](const char *msg, rtosc::RtData &d) { #define rEnd } rtosc::Ports Alienwah::ports = { {"preset::i", rProp(parameter) rOptions(wah 1, wah 2, wah 3, wah 4) rDoc("Instrument Presets"), 0, rBegin; rObject *o = (rObject*)d.obj; if(rtosc_narguments(msg)) o->setpreset(rtosc_argument(msg, 0).i); else d.reply(d.loc, "i", o->Ppreset); rEnd}, rEffParVol(rDefault(127), rPresetsAt(3, 93)), rEffParPan(), rEffPar(Pfreq, 2, rShort("freq") rPresets(70, 73, 63, 25), "Effect Frequency"), rEffPar(Pfreqrnd, 3, rShort("rand"), rPreset(1, 106) rDefault(0), "Frequency Randomness"), rEffPar(PLFOtype, 4, rShort("shape"), rOptions(sine, triangle), rPresets(sine, sine, triangle, triangle), "LFO Shape"), rEffPar(PStereo, 5, rShort("stereo"), rPresets(62, 101, 100, 66), "Stereo Mode"), rEffPar(Pdepth, 6, rShort("depth"), rPresets(60, 60, 112, 101), "LFO Depth"), rEffPar(Pfeedback, 7, rShort("fb"), rPreset(3, 11), rDefault(105), "Feedback"), rEffPar(Pdelay, 8, rLinear(1,100), rPresets(25, 17, 31, 47), rShort("delay"), "Delay"), rEffPar(Plrcross, 9, rShort("l/r"), rDefault(0), "Left/Right Crossover"), rEffPar(Pphase, 10, rShort("phase"), rDefault(64), rPreset(2, 42), rPreset(3, 86), "Phase"), }; #undef rBegin #undef rEnd #undef rObject Alienwah::Alienwah(EffectParams pars) :Effect(pars), lfo(pars.srate, pars.bufsize), oldl(NULL), oldr(NULL) { setpreset(Ppreset); cleanup(); oldclfol = complex(fb, 0.0f); oldclfor = complex(fb, 0.0f); } Alienwah::~Alienwah() { memory.devalloc(oldl); memory.devalloc(oldr); } //Apply the effect void Alienwah::out(const Stereo &smp) { float lfol, lfor; //Left/Right LFOs complex clfol, clfor; /**\todo Rework, as optimization can be used when the new complex type is * utilized. * Before all calculations needed to be done with individual float, * but now they can be done together*/ lfo.effectlfoout(&lfol, &lfor); lfol *= depth * PI * 2.0f; lfor *= depth * PI * 2.0f; clfol = complex(cosf(lfol + phase) * fb, sinf(lfol + phase) * fb); //rework clfor = complex(cosf(lfor + phase) * fb, sinf(lfor + phase) * fb); //rework for(int i = 0; i < buffersize; ++i) { float x = ((float) i) / buffersize_f; float x1 = 1.0f - x; //left complex tmp = clfol * x + oldclfol * x1; complex out = tmp * oldl[oldk]; out += (1 - fabs(fb)) * smp.l[i] * pangainL; oldl[oldk] = out; float l = out.real() * 10.0f * (fb + 0.1f); //right tmp = clfor * x + oldclfor * x1; out = tmp * oldr[oldk]; out += (1 - fabs(fb)) * smp.r[i] * pangainR; oldr[oldk] = out; float r = out.real() * 10.0f * (fb + 0.1f); if(++oldk >= Pdelay) oldk = 0; //LRcross efxoutl[i] = l * (1.0f - lrcross) + r * lrcross; efxoutr[i] = r * (1.0f - lrcross) + l * lrcross; } oldclfol = clfol; oldclfor = clfor; } //Cleanup the effect void Alienwah::cleanup(void) { for(int i = 0; i < Pdelay; ++i) { oldl[i] = complex(0.0f, 0.0f); oldr[i] = complex(0.0f, 0.0f); } oldk = 0; } //Parameter control void Alienwah::setdepth(unsigned char _Pdepth) { Pdepth = _Pdepth; depth = Pdepth / 127.0f; } void Alienwah::setfb(unsigned char _Pfb) { Pfb = _Pfb; fb = fabs((Pfb - 64.0f) / 64.1f); fb = sqrtf(fb); if(fb < 0.4f) fb = 0.4f; if(Pfb < 64) fb = -fb; } void Alienwah::setvolume(unsigned char _Pvolume) { Pvolume = _Pvolume; outvolume = Pvolume / 127.0f; if(insertion == 0) volume = 1.0f; else volume = outvolume; } void Alienwah::setphase(unsigned char _Pphase) { Pphase = _Pphase; phase = (Pphase - 64.0f) / 64.0f * PI; } void Alienwah::setdelay(unsigned char _Pdelay) { memory.devalloc(oldl); memory.devalloc(oldr); Pdelay = limit(_Pdelay, 1, MAX_ALIENWAH_DELAY); oldl = memory.valloc>(Pdelay); oldr = memory.valloc>(Pdelay); cleanup(); } void Alienwah::setpreset(unsigned char npreset) { const int PRESET_SIZE = 11; const int NUM_PRESETS = 4; unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { //AlienWah1 {127, 64, 70, 0, 0, 62, 60, 105, 25, 0, 64}, //AlienWah2 {127, 64, 73, 106, 0, 101, 60, 105, 17, 0, 64}, //AlienWah3 {127, 64, 63, 0, 1, 100, 112, 105, 31, 0, 42}, //AlienWah4 {93, 64, 25, 0, 1, 66, 101, 11, 47, 0, 86} }; if(npreset >= NUM_PRESETS) npreset = NUM_PRESETS - 1; for(int n = 0; n < PRESET_SIZE; ++n) changepar(n, presets[npreset][n]); if(insertion == 0) changepar(0, presets[npreset][0] / 2); //lower the volume if this is system effect Ppreset = npreset; } void Alienwah::changepar(int npar, unsigned char value) { switch(npar) { case 0: setvolume(value); break; case 1: setpanning(value); break; case 2: lfo.Pfreq = value; lfo.updateparams(); break; case 3: lfo.Prandomness = value; lfo.updateparams(); break; case 4: lfo.PLFOtype = value; lfo.updateparams(); break; case 5: lfo.Pstereo = value; lfo.updateparams(); break; case 6: setdepth(value); break; case 7: setfb(value); break; case 8: setdelay(value); break; case 9: setlrcross(value); break; case 10: setphase(value); break; } } unsigned char Alienwah::getpar(int npar) const { switch(npar) { case 0: return Pvolume; case 1: return Ppanning; case 2: return lfo.Pfreq; case 3: return lfo.Prandomness; case 4: return lfo.PLFOtype; case 5: return lfo.Pstereo; case 6: return Pdepth; case 7: return Pfb; case 8: return Pdelay; case 9: return Plrcross; case 10: return Pphase; default: return 0; } } }