#pragma once #include using namespace rack; extern Plugin* pluginInstance; extern Model* modelEvenVCO; extern Model* modelEvenVCO2; extern Model* modelRampage; extern Model* modelABC; extern Model* modelSpringReverb; extern Model* modelMixer; extern Model* modelSlewLimiter; extern Model* modelDualAtenuverter; extern Model* modelPercall; extern Model* modelHexmixVCA; extern Model* modelChoppingKinky; extern Model* modelKickall; extern Model* modelSamplingModulator; extern Model* modelMorphader; extern Model* modelADSR; extern Model* modelSTMix; extern Model* modelMuxlicer; extern Model* modelMex; extern Model* modelNoisePlethora; extern Model* modelChannelStrip; extern Model* modelPonyVCO; extern Model* modelMotionMTR; extern Model* modelBurst; extern Model* modelMidiThing; extern Model* modelVoltio; struct Knurlie : SvgScrew { Knurlie() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Knurlie.svg"))); } }; struct BefacoTinyKnobWhite : BefacoTinyKnob { BefacoTinyKnobWhite() {} }; struct BefacoTinyKnobRed : BefacoTinyKnob { BefacoTinyKnobRed() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoTinyPointWhite.svg"))); bg->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoTinyKnobRed_bg.svg"))); } }; struct BefacoTinyKnobDarkGrey : BefacoTinyKnob { BefacoTinyKnobDarkGrey() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoTinyPointWhite.svg"))); bg->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoTinyKnobDarkGrey_bg.svg"))); } }; struct BefacoTinyKnobLightGrey : BefacoTinyKnob { BefacoTinyKnobLightGrey() { bg->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoTinyKnobLightGrey_bg.svg"))); } }; struct BefacoTinyKnobBlack : BefacoTinyKnob { BefacoTinyKnobBlack() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoTinyPointWhite.svg"))); bg->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoTinyKnobBlack_bg.svg"))); } }; struct Davies1900hLargeGreyKnob : Davies1900hKnob { Davies1900hLargeGreyKnob() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hLargeGrey.svg"))); bg->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hLargeGrey_bg.svg"))); } }; struct Davies1900hLightGreyKnob : Davies1900hKnob { Davies1900hLightGreyKnob() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hLightGrey.svg"))); bg->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hLightGrey_bg.svg"))); } }; struct Davies1900hDarkGreyKnob : Davies1900hKnob { Davies1900hDarkGreyKnob() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hDarkGrey.svg"))); bg->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hDarkGrey_bg.svg"))); } }; /** Deprecated alias */ using Davies1900hDarkBlackAlt = Davies1900hBlackKnob; struct BananutRed : app::SvgPort { BananutRed() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BananutRed.svg"))); } }; /** Deprecated alias */ using BefacoOutputPort = BananutRed; struct BananutBlack : app::SvgPort { BananutBlack() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BananutBlack.svg"))); } }; /** Deprecated alias */ using BefacoInputPort = BananutBlack; struct CKSSNarrow : app::SvgSwitch { CKSSNarrow() { addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchNarrow_0.svg"))); addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchNarrow_2.svg"))); } }; struct Crossfader : app::SvgSlider { Crossfader() { setBackgroundSvg(Svg::load(asset::plugin(pluginInstance, "res/components/CrossfaderBackground.svg"))); setHandleSvg(Svg::load(asset::plugin(pluginInstance, "res/components/CrossfaderHandle.svg"))); minHandlePos = mm2px(Vec(4.5f, -0.8f)); maxHandlePos = mm2px(Vec(34.5, -0.8f)); horizontal = true; math::Vec margin = math::Vec(15, 5); background->box.pos = margin; box.size = background->box.size.plus(margin.mult(2)); } }; struct BefacoSwitchHorizontal : app::SvgSwitch { BefacoSwitchHorizontal() { addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoSwitchHoriz_0.svg"))); addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoSwitchHoriz_1.svg"))); addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoSwitchHoriz_2.svg"))); } }; struct CKSSHoriz2 : app::SvgSwitch { CKSSHoriz2() { addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchNarrowHoriz_0.svg"))); addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchNarrowHoriz_1.svg"))); } }; struct CKSSVert7 : app::SvgSlider { CKSSVert7() { math::Vec margin = math::Vec(3.5, 3.5); maxHandlePos = math::Vec(1, 1).plus(margin); minHandlePos = math::Vec(1, 45).plus(margin); setBackgroundSvg(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchTallVert_bg.svg"))); setHandleSvg(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchTallVert_fg.svg"))); background->box.pos = margin; box.size = background->box.size.plus(margin.mult(2)); } // disable double click as this messes with click to advance void onDoubleClick(const event::DoubleClick& e) override { } // cycle through the values (with reset) on click only (not drag) void onAction(const ActionEvent& e) override { ParamQuantity* paramQuantity = getParamQuantity(); float range = paramQuantity->maxValue - paramQuantity->minValue; float newValue = paramQuantity->getValue() + 1.f; if (newValue > paramQuantity->maxValue) { newValue -= range + 1.f; } paramQuantity->setValue(newValue); } }; struct CKSSHoriz4 : app::SvgSlider { CKSSHoriz4() { setBackgroundSvg(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchWideHoriz_bg.svg"))); setHandleSvg(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchWideHoriz_fg.svg"))); minHandlePos = mm2px(Vec(0.3f, 0.3f)); maxHandlePos = mm2px(Vec(6.3f, 0.3f)); horizontal = true; math::Vec margin = math::Vec(0, 0); background->box.pos = margin; box.size = background->box.size.plus(margin.mult(2)); } // disable double click as this messes with click to advance void onDoubleClick(const event::DoubleClick& e) override { } // cycle through the values (with reset) on click only (not drag) void onAction(const ActionEvent& e) override { ParamQuantity* paramQuantity = getParamQuantity(); float range = paramQuantity->maxValue - paramQuantity->minValue; float newValue = paramQuantity->getValue() + 1.f; if (newValue > paramQuantity->maxValue) { newValue -= range + 1.f; } paramQuantity->setValue(newValue); } }; struct CKSSNarrow3 : app::SvgSwitch { CKSSNarrow3() { addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchNarrow_0.svg"))); addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchNarrow_1.svg"))); addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/SwitchNarrow_2.svg"))); } }; struct Davies1900hLargeLightGreyKnob : Davies1900hKnob { Davies1900hLargeLightGreyKnob() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hLargeLightGrey.svg"))); bg->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hLargeLightGrey_bg.svg"))); } }; struct BefacoSlidePotSmall : app::SvgSlider { BefacoSlidePotSmall() { math::Vec margin = math::Vec(3.5, 3.5); maxHandlePos = math::Vec(-2, -2).plus(margin); minHandlePos = math::Vec(-2, 60).plus(margin); setBackgroundSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoSlidePotSmall.svg"))); setHandleSvg(Svg::load(asset::plugin(pluginInstance, "res/components/BefacoSlidePotHandleSmall.svg"))); background->box.pos = margin; box.size = background->box.size.plus(margin.mult(2)); } }; struct BefacoButton : app::SvgSwitch { BefacoButton() { momentary = true; addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/BefacoButton_0.svg"))); addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/BefacoButton_1.svg"))); } }; struct Davies1900hWhiteKnobEndless : Davies1900hKnob { Davies1900hWhiteKnobEndless() { setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hWhiteEndless.svg"))); bg->setSvg(Svg::load(asset::plugin(pluginInstance, "res/components/Davies1900hWhiteEndless_bg.svg"))); } }; inline int unsigned_modulo(int a, int b) { return ((a % b) + b) % b; } template T sin2pi_pade_05_5_4(T x) { x -= 0.5f; return (T(-6.283185307) * x + T(33.19863968) * simd::pow(x, 3) - T(32.44191367) * simd::pow(x, 5)) / (1 + T(1.296008659) * simd::pow(x, 2) + T(0.7028072946) * simd::pow(x, 4)); } template T tanh_pade(T x) { T x2 = x * x; T q = 12.f + x2; return 12.f * x * q / (36.f * x2 + q * q); } template T exponentialBipolar80Pade_5_4(T x) { return (T(0.109568) * x + T(0.281588) * simd::pow(x, 3) + T(0.133841) * simd::pow(x, 5)) / (T(1.) - T(0.630374) * simd::pow(x, 2) + T(0.166271) * simd::pow(x, 4)); } struct ADEnvelope { enum Stage { STAGE_OFF, STAGE_ATTACK, STAGE_DECAY }; Stage stage = STAGE_OFF; float env = 0.f; float attackTime = 0.1, decayTime = 0.1; float attackShape = 1.0, decayShape = 1.0; ADEnvelope() { }; void process(const float& sampleTime) { if (stage == STAGE_OFF) { env = envLinear = 0.0f; } else if (stage == STAGE_ATTACK) { envLinear += sampleTime / attackTime; env = std::pow(envLinear, attackShape); } else if (stage == STAGE_DECAY) { envLinear -= sampleTime / decayTime; env = std::pow(envLinear, decayShape); } if (envLinear >= 1.0f) { stage = STAGE_DECAY; env = envLinear = 1.0f; } else if (envLinear <= 0.0f) { stage = STAGE_OFF; env = envLinear = 0.0f; } } void trigger() { stage = ADEnvelope::STAGE_ATTACK; // non-linear envelopes won't retrigger at the correct starting point if // attackShape != decayShape, so we advance the linear envelope envLinear = std::pow(env, 1.0f / attackShape); } private: float envLinear = 0.f; }; // Creates a Butterworth 2*Nth order highpass filter for blocking DC template struct DCBlockerT { DCBlockerT() { setFrequency(0.1f); } // set frequency (in normalised units, 0 < fc < 1) void setFrequency(float fc) { fc_ = fc; recalculateCoefficients(); } float process(float x) { for (int idx = 0; idx < N; idx++) { x = blockDCFilter[idx].process(x); } return x; } private: // https://www.earlevel.com/main/2016/09/29/cascading-filters/ void recalculateCoefficients() { float poleInc = M_PI / order; float firstAngle = poleInc / 2; for (int idx = 0; idx < N; idx++) { float Q = 1.0f / (2.0f * std::cos(firstAngle + idx * poleInc)); blockDCFilter[idx].setParameters(dsp::BiquadFilter::HIGHPASS, fc_, Q, 1.0f); } } float fc_; static const int order = 2 * N; dsp::BiquadFilter blockDCFilter[N]; }; typedef DCBlockerT<2> DCBlocker; /** When triggered, holds a high value for a specified time before going low again */ struct PulseGenerator_4 { simd::float_4 remaining = 0.f; /** Immediately disables the pulse */ void reset() { remaining = 0.f; } /** Advances the state by `deltaTime`. Returns whether the pulse is in the HIGH state. */ simd::float_4 process(float deltaTime) { simd::float_4 mask = (remaining > 0.f); remaining -= ifelse(mask, deltaTime, 0.f); return ifelse(mask, simd::float_4::mask(), 0.f); } /** Begins a trigger with the given `duration`. */ void trigger(simd::float_4 mask, float duration = 1e-3f) { // Keep the previous pulse if the existing pulse will be held longer than the currently requested one. remaining = ifelse(mask & (duration > remaining), duration, remaining); } }; // Zavalishin 2018, "The Art of VA Filter Design", http://www.native-instruments.com/fileadmin/ni_media/downloads/pdf/VAFilterDesign_2.0.0a.pdf // Section 6.7, adopted from BogAudio Saturator https://github.com/bogaudio/BogaudioModules/blob/master/src/dsp/signal.cpp template struct Saturator { // saturate input at around ~[-1, +1] V with soft clipping static T process(T sample) { return simd::ifelse(sample < 0.f, -saturation(-sample), saturation(sample)); } private: static T saturation(T sample) { const float limit = 1.05f; const float y1 = 0.98765f; // (2*x - 1)/x**2 where x is 0.9. // correction so that saturation(0) = 0 const float offset = 0.0062522; // -0.5f + sqrtf(0.5f * 0.5f) / y1; T x = sample / limit; T x1 = (x + 1.0f) * 0.5f; return limit * (offset + x1 - simd::sqrt(x1 * x1 - y1 * x) * (1.0f / y1)); } };