/* * ZamDelay mono delay effect * Copyright (C) 2015 Damien Zammit * * 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 any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the GNU General Public License see the doc/GPL.txt file. */ #include "ZamDelayPlugin.hpp" START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- ZamDelayPlugin::ZamDelayPlugin() : Plugin(paramCount, 1, 0) { // set default values loadProgram(0); } // ----------------------------------------------------------------------- // Init void ZamDelayPlugin::initParameter(uint32_t index, Parameter& parameter) { switch (index) { case paramInvert: parameter.hints = kParameterIsAutomable | kParameterIsBoolean; parameter.name = "Invert"; parameter.symbol = "inv"; parameter.unit = " "; parameter.ranges.def = 0.0f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; break; case paramDelaytime: parameter.hints = kParameterIsAutomable; parameter.name = "Time"; parameter.symbol = "time"; parameter.unit = "ms"; parameter.ranges.def = 160.0f; parameter.ranges.min = 1.0f; parameter.ranges.max = 8000.0f; break; case paramSync: parameter.hints = kParameterIsAutomable | kParameterIsBoolean; parameter.name = "Sync BPM"; parameter.symbol = "sync"; parameter.unit = " "; parameter.ranges.def = 0.0f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; break; case paramLPF: parameter.hints = kParameterIsAutomable; parameter.name = "LPF"; parameter.symbol = "lpf"; parameter.unit = "Hz"; parameter.ranges.def = 6000.0f; parameter.ranges.min = 20.0f; parameter.ranges.max = 20000.0f; break; case paramDivisor: parameter.hints = kParameterIsAutomable | kParameterIsInteger; parameter.name = "Divisor"; parameter.symbol = "div"; parameter.unit = " "; parameter.ranges.def = 3.0f; parameter.ranges.min = 1.0f; parameter.ranges.max = 5.0f; break; case paramGain: parameter.hints = kParameterIsAutomable; parameter.name = "Output Gain"; parameter.symbol = "gain"; parameter.unit = "dB"; parameter.ranges.def = 0.0f; parameter.ranges.min = -60.0f; parameter.ranges.max = 0.0f; break; case paramDrywet: parameter.hints = kParameterIsAutomable; parameter.name = "Dry/Wet"; parameter.symbol = "drywet"; parameter.unit = " "; parameter.ranges.def = 0.5f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; break; case paramFeedback: parameter.hints = kParameterIsAutomable; parameter.name = "Feedback"; parameter.symbol = "feedb"; parameter.unit = " "; parameter.ranges.def = 0.0f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; break; case paramDelaytimeout: parameter.hints = kParameterIsOutput; parameter.name = "Delaytime"; parameter.symbol = "delaytime"; parameter.unit = "ms"; parameter.ranges.def = 160.0f; parameter.ranges.min = 1.0f; parameter.ranges.max = 8000.0f; break; } } void ZamDelayPlugin::initProgramName(uint32_t index, String& programName) { switch(index) { case 0: programName = "Zero"; break; } } void ZamDelayPlugin::loadProgram(uint32_t index) { switch(index) { case 0: invert = 0.f; delaytime = 160.f; sync = 0.f; lpf = 6000.f; divisor = 3.f; gain = 0.f; drywet = 0.5f; feedb = 0.0f; delaytimeout = 160.f; break; } activate(); } // ----------------------------------------------------------------------- // Internal data float ZamDelayPlugin::getParameterValue(uint32_t index) const { switch (index) { case paramInvert: return invert; break; case paramDelaytime: return delaytime; break; case paramSync: return sync; break; case paramLPF: return lpf; break; case paramDivisor: return divisor; break; case paramGain: return gain; break; case paramDrywet: return drywet; break; case paramFeedback: return feedb; break; case paramDelaytimeout: return delaytimeout; break; default: return 0.0f; } } void ZamDelayPlugin::setParameterValue(uint32_t index, float value) { switch (index) { case paramInvert: invert = value; break; case paramDelaytime: delaytime = value; break; case paramSync: sync = value; break; case paramLPF: lpf = value; break; case paramDivisor: divisor = value; break; case paramGain: gain = value; break; case paramDrywet: drywet = value; break; case paramFeedback: feedb = value; break; case paramDelaytimeout: delaytimeout = value; break; } } // ----------------------------------------------------------------------- // Process void ZamDelayPlugin::activate() { int i; for (i = 0; i < MAX_DELAY; i++) { z[i] = 0.f; } posz = 0; tap[0] = 0; tap[1] = 0; active = 0; next = 1; age = 0; clearfilter(); fbstate = 0.f; lpfold = 0.f; divisorold = 0.f; gainold = 0.f; invertold = 0.f; delaytimeold = 0.f; syncold = 0.f; drywetold = 0.f; delaytimeoutold = 0.f; delaysamplesold = 1.f; } void ZamDelayPlugin::lpfRbj(float fc, float srate) { float w0, alpha, cw, sw, q; q = 0.707; w0 = (2. * M_PI * fc / srate); sw = sin(w0); cw = cos(w0); alpha = sw / (2. * q); A0 = 1. + alpha; A1 = -2. * cw; A2 = 1. - alpha; B0 = (1. - cw) / 2.; B1 = (1. - cw); B2 = B0; A3 = 1. + alpha; A4 = -2. * cw; A5 = 1. - alpha; B3 = (1. - cw) / 2.; B4 = (1. - cw); B5 = B3; } void ZamDelayPlugin::clearfilter(void) { state[0] = state[1] = state[2] = state[3] = 0.f; } float ZamDelayPlugin::runfilter(float in) { float out; in = sanitize_denormal(in); out = B0/A0*in + B1/A0*state[0] + B2/A0*state[1] -A1/A0*state[2] - A2/A0*state[3] + 1e-12; state[1] = state[0]; state[0] = in; state[3] = state[2]; state[2] = out; return out; } void ZamDelayPlugin::run(const float** inputs, float** outputs, uint32_t frames) { uint32_t i; float in; float srate = getSampleRate(); TimePosition t = getTimePosition(); float bpm = 120.f; int delaysamples; unsigned int tmp; float inv; float xfade; int recalc; if (invert < 0.5) { inv = -1.f; } else { inv = 1.f; } recalc = 0; delaytimeout = delaytime; if (t.bbt.valid) { bpm = t.bbt.beatsPerMinute; if (sync > 0.5f) { delaytimeout = (float)t.bbt.beatType * 1000.f * 60.f / (bpm * powf(2., divisor - 1.)); } } delaysamples = (int)(delaytimeout * srate) / 1000; if (lpf != lpfold) { lpfRbj(lpf, srate); } if (divisor != divisorold) { recalc = 1; } if (gain != gainold) { recalc = 1; } if (invert != invertold) { recalc = 1; } if (delaytime != delaytimeold) { recalc = 1; } if (sync != syncold) { recalc = 1; } if (delaytimeout != delaytimeoutold) { recalc = 1; } if (recalc) { tap[next] = delaysamples; } xfade = 0.f; for (i = 0; i < frames; i++) { in = inputs[0][i]; z[posz] = in + feedb * fbstate; fbstate = 0.f; int p = posz - tap[active]; // active line if (p<0) p += MAX_DELAY; fbstate += z[p]; if (recalc) { xfade += 1.0f / (float)frames; fbstate *= (1.-xfade); int p = posz - tap[next]; // next line if (p<0) p += MAX_DELAY; fbstate += z[p] * xfade; } outputs[0][i] = from_dB(gain) * ((1.-drywet)*in + drywet * -inv * runfilter(fbstate)); if (++posz >= MAX_DELAY) { posz = 0; } } lpfold = lpf; divisorold = divisor; gainold = gain; invertold = invert; delaytimeold = delaytime; syncold = sync; drywetold = drywet; delaytimeoutold = delaytimeout; delaysamplesold = delaysamples; if (recalc) { tmp = active; active = next; next = tmp; } } // ----------------------------------------------------------------------- Plugin* createPlugin() { return new ZamDelayPlugin(); } // ----------------------------------------------------------------------- END_NAMESPACE_DISTRHO