#include "Southpole.hpp" #include "dsp/digital.hpp" namespace rack_plugin_Southpole { #define NBUF 6 struct Rakes : Module { enum ParamIds { DECAY_PARAM, QUANT_PARAM, MIX_PARAM, TUNE1_PARAM, TUNE2_PARAM, TUNE3_PARAM, TUNE4_PARAM, TUNE5_PARAM, TUNE6_PARAM, FINE1_PARAM, FINE2_PARAM, FINE3_PARAM, FINE4_PARAM, FINE5_PARAM, FINE6_PARAM, GAIN1_PARAM, GAIN2_PARAM, GAIN3_PARAM, GAIN4_PARAM, GAIN5_PARAM, GAIN6_PARAM, NUM_PARAMS }; enum InputIds { INL_INPUT, INR_INPUT, DECAY_INPUT, MIX_INPUT, TUNE1_INPUT, TUNE2_INPUT, TUNE3_INPUT, TUNE4_INPUT, TUNE5_INPUT, TUNE6_INPUT, //GAIN1_INPUT,GAIN2_INPUT, //GAIN3_INPUT,GAIN4_INPUT, //GAIN5_INPUT,GAIN6_INPUT, NUM_INPUTS }; enum OutputIds { OUTL_OUTPUT, OUTR_OUTPUT, NUM_OUTPUTS }; enum LightIds { NUM_LIGHTS }; //SchmittTrigger clock; float *bufl[NBUF]; float *bufr[NBUF]; int maxsize; int headl[NBUF]; int headr[NBUF]; int sizel[NBUF]; int sizer[NBUF]; int lastsizel[NBUF]; int lastsizer[NBUF]; Rakes() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { maxsize = engineGetSampleRate(); for (int j=0; j < NBUF; j++) { bufl[j] = new float [maxsize]; bufr[j] = new float [maxsize]; for (int i=0; i < maxsize; i++) { bufl[j][i] = 0; bufr[j][i] = 0; } headl[j] = 0; headr[j] = 0; } } float xm1 = 0; float ym1 = 0; float dcblock( float x ) { float y = x - xm1 + 0.995 * ym1; xm1 = x; ym1 = y; return y; } void step() override; }; void Rakes::step() { //float mix = clamp(params[MIX_PARAM].value + inputs[MIX_INPUT].normalize(0.) / 10.0, 0.0, 1.0); float mix = params[MIX_PARAM].value; float rate = clamp(params[DECAY_PARAM].value + inputs[DECAY_INPUT].normalize(0.) / 10.0, 0.0f, .99f); const float f0 = 261.626; float inl = inputs[INL_INPUT].normalize(0.); float inr = inputs[INR_INPUT].normalize(inl); float sumoutl = 0; float sumoutr = 0; float sumgain = 1.; for (int j=0; j < NBUF; j++) { //float gain = clamp(params[GAIN1_PARAM + j].value + inputs[GAIN1_INPUT + j].normalize(0.) / 10.0, 0.0, 1.0); float gain = params[GAIN1_PARAM + j].value; if (gain < 1e-3) continue; sumgain += gain; float tune = clamp(params[TUNE1_PARAM + j].value + inputs[TUNE1_INPUT + j].normalize(0.), -5.0, 5.5); float fine = clamp(params[FINE1_PARAM + j].value, -1.0, 1.0); if ( params[QUANT_PARAM].value > 0.5 ) { tune = round(12.*tune)/12.; } float freql = f0 * powf(2., tune + fine/12.); float freqr = f0 * powf(2., tune - fine/12.); // key follow //float fb = crossfade(f0, freq, follow); // full follow decay rate is T60 time float fbl = pow(10, -3./freql/fabs(5.*rate)); float fbr = pow(10, -3./freqr/fabs(5.*rate)); //fb = fb * ((0. < rate) - (rate < 0.)); //printf("%f %f %f\n",lfreq,rate,fb); sizel[j] = maxsize / freqr; sizer[j] = maxsize / freql; if (sizel[j] > lastsizel[j]) { for (int i=sizel[j]; i < lastsizel[j]; i++ ) bufl[i]=0; } if (sizel[j] > lastsizer[j]) { for (int i=sizer[j]; i < lastsizer[j]; i++ ) bufr[i]=0; } lastsizel[j] = maxsize / freqr; lastsizer[j] = maxsize / freql; float outl = bufl[j][headl[j]]; float outr = bufr[j][headr[j]]; bufl[j][headl[j]] = inl + fbl * outl; bufr[j][headr[j]] = inr + fbr * outr; headl[j]++; headr[j]++; if (headl[j] > sizel[j]) { headl[j] = 0; } if (headr[j] > sizer[j]) { headr[j] = 0; } sumoutl += gain*outl; sumoutr += gain*outr; } sumoutl = clamp( dcblock(sumoutl) / sumgain, -10., 10.); //in + gain*out; sumoutr = clamp( dcblock(sumoutr) / sumgain, -10., 10.); //in + gain*out; outputs[OUTL_OUTPUT].value = crossfade(inl,sumoutl,mix); outputs[OUTR_OUTPUT].value = crossfade(inr,sumoutr,mix); } struct RakesWidget : ModuleWidget { RakesWidget(Rakes *module) : ModuleWidget(module) { box.size = Vec(15*8, 380); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/Rakes.svg"))); addChild(panel); } const float x1 = 5.; const float x2 = 35.; const float x3 = 65.; const float x4 = 95.; const float y1 = 40.; const float yh = 32.; addInput(Port::create(Vec(x2, y1+0*yh), Port::INPUT, module, Rakes::DECAY_INPUT)); addParam(ParamWidget::create(Vec(x3, y1+0*yh), module, Rakes::DECAY_PARAM, 0.0, 1.0, 0.0)); //addParam(ParamWidget::create (Vec(x3, y1+0*yh), module, Rakes::FOLLOW_PARAM, 0.0, 1.0, 0.0)); for (int j=0; j < NBUF; j++) { addInput(Port::create (Vec(x1, y1+(j+1.5)*yh), Port::INPUT, module, Rakes::TUNE1_INPUT + j)); addParam(ParamWidget::create (Vec(x2, y1+(j+1.5)*yh), module, Rakes::TUNE1_PARAM + j, -5.0, 5.5, 0.0)); addParam(ParamWidget::create (Vec(x3, y1+(j+1.5)*yh), module, Rakes::FINE1_PARAM + j, -1.0, 1.0, 0.0)); addParam(ParamWidget::create (Vec(x4, y1+(j+1.5)*yh), module, Rakes::GAIN1_PARAM + j, 0.0, 1.0, 0.0)); } addInput(Port::create (Vec(x1, y1+8*yh), Port::INPUT, module, Rakes::INL_INPUT)); addInput(Port::create (Vec(x1, y1+9*yh), Port::INPUT, module, Rakes::INR_INPUT)); addParam(ParamWidget::create( Vec(x2, y1+7.5*yh), module, Rakes::QUANT_PARAM, 0.0, 1.0, 0.0)); addParam(ParamWidget::create (Vec((x2+x3)/2., y1+8.5*yh), module, Rakes::MIX_PARAM, 0.0, 1.0, 0.5)); addOutput(Port::create (Vec(x4, y1+8*yh), Port::OUTPUT, module, Rakes::OUTL_OUTPUT)); addOutput(Port::create (Vec(x4, y1+9*yh), Port::OUTPUT, module, Rakes::OUTR_OUTPUT)); } }; } // namespace rack_plugin_Southpole using namespace rack_plugin_Southpole; RACK_PLUGIN_MODEL_INIT(Southpole, Rakes) { Model *modelRakes = Model::create( "Southpole", "Rakes", "Rakes - resonator bank", FILTER_TAG); return modelRakes; }