XCO.hpp (6443B)
1 #pragma once 2 3 #include "bogaudio.hpp" 4 #include "dsp/filters/resample.hpp" 5 #include "dsp/oscillator.hpp" 6 #include "dsp/signal.hpp" 7 8 using namespace bogaudio::dsp; 9 10 extern Model* modelXCO; 11 12 namespace bogaudio { 13 14 struct XCO : BGModule { 15 enum ParamsIds { 16 FREQUENCY_PARAM, 17 FINE_PARAM, 18 SLOW_PARAM, 19 FM_DEPTH_PARAM, 20 FM_TYPE_PARAM, 21 SQUARE_PW_PARAM, 22 SQUARE_PHASE_PARAM, 23 SQUARE_MIX_PARAM, 24 SAW_SATURATION_PARAM, 25 SAW_PHASE_PARAM, 26 SAW_MIX_PARAM, 27 TRIANGLE_SAMPLE_PARAM, 28 TRIANGLE_PHASE_PARAM, 29 TRIANGLE_MIX_PARAM, 30 SINE_FEEDBACK_PARAM, 31 SINE_PHASE_PARAM, 32 SINE_MIX_PARAM, 33 NUM_PARAMS 34 }; 35 36 enum InputsIds { 37 FM_INPUT, 38 FM_DEPTH_INPUT, 39 SQUARE_PW_INPUT, 40 SQUARE_PHASE_INPUT, 41 SQUARE_MIX_INPUT, 42 SAW_SATURATION_INPUT, 43 SAW_PHASE_INPUT, 44 SAW_MIX_INPUT, 45 TRIANGLE_SAMPLE_INPUT, 46 TRIANGLE_PHASE_INPUT, 47 TRIANGLE_MIX_INPUT, 48 SINE_FEEDBACK_INPUT, 49 SINE_PHASE_INPUT, 50 SINE_MIX_INPUT, 51 PITCH_INPUT, 52 SYNC_INPUT, 53 NUM_INPUTS 54 }; 55 56 enum OutputsIds { 57 SQUARE_OUTPUT, 58 SAW_OUTPUT, 59 TRIANGLE_OUTPUT, 60 SINE_OUTPUT, 61 MIX_OUTPUT, 62 NUM_OUTPUTS 63 }; 64 65 enum Clipping { 66 NO_CLIPPING = 0, 67 COMP_CLIPPING, 68 SOFT_CLIPPING, 69 HARD_CLIPPING 70 }; 71 72 struct Engine { 73 static constexpr int oversample = 8; 74 75 float frequency = 0.0f; 76 float baseVOct = 0.0f; 77 float baseHz = 0.0f; 78 float fmDepth = 0.0f; 79 float triangleSampleWidth = 0.0f; 80 float sineFeedback = 0.0f; 81 float sineOMix = 0.0f; 82 float sineFeedbackDelayedSample = 0.0f; 83 Phasor::phase_delta_t squarePhaseOffset = 0.0f; 84 Phasor::phase_delta_t sawPhaseOffset = 0.0f; 85 Phasor::phase_delta_t trianglePhaseOffset = 0.0f; 86 Phasor::phase_delta_t sinePhaseOffset = 0.0f; 87 float squareMix = 1.0f; 88 float sawMix = 1.0f; 89 float triangleMix = 1.0f; 90 float sineMix = 1.0f; 91 float lastCycle = 0.0f; 92 float minMix = 0.0f; 93 float maxMix = 0.0f; 94 float mixScale = 0.25; 95 96 Phasor phasor; 97 BandLimitedSquareOscillator square; 98 BandLimitedSawOscillator saw; 99 TriangleOscillator triangle; 100 SineTableOscillator sine; 101 CICDecimator squareDecimator; 102 CICDecimator sawDecimator; 103 CICDecimator triangleDecimator; 104 CICDecimator sineDecimator; 105 float squareBuffer[oversample]; 106 float sawBuffer[oversample]; 107 float triangleBuffer[oversample]; 108 float sineBuffer[oversample]; 109 PositiveZeroCrossing syncTrigger; 110 Saturator saturator; 111 112 bogaudio::dsp::SlewLimiter fmDepthSL; 113 bogaudio::dsp::SlewLimiter squarePulseWidthSL; 114 bogaudio::dsp::SlewLimiter sawSaturationSL; 115 bogaudio::dsp::SlewLimiter triangleSampleWidthSL; 116 bogaudio::dsp::SlewLimiter sineFeedbackSL; 117 bogaudio::dsp::SlewLimiter squareMixSL; 118 bogaudio::dsp::SlewLimiter sawMixSL; 119 bogaudio::dsp::SlewLimiter triangleMixSL; 120 bogaudio::dsp::SlewLimiter sineMixSL; 121 122 Engine() { 123 saw.setQuality(12); 124 square.setQuality(12); 125 } 126 127 void reset(); 128 void sampleRateChange(float sampleRate); 129 void setFrequency(float frequency); 130 }; 131 132 const float amplitude = 5.0f; 133 const float _slowModeOffset = -7.0f; 134 const float sineOversampleMixIncrement = 0.01f; 135 float _oversampleThreshold = 0.0f; 136 bool _slowMode = false; 137 bool _fmLinearMode = false; 138 bool _dcCorrection = true; 139 Clipping _clippingMode = COMP_CLIPPING; 140 Engine* _engines[maxChannels] {}; 141 142 struct XCOFrequencyParamQuantity : FrequencyParamQuantity { 143 float offset() override; 144 }; 145 146 XCO() { 147 config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); 148 configParam<XCOFrequencyParamQuantity>(FREQUENCY_PARAM, -3.0f, 6.0f, 0.0f, "Frequency", " Hz"); 149 configParam(FINE_PARAM, -1.0f, 1.0f, 0.0f, "Fine tune", " cents", 0.0f, 100.0f); 150 configButton(SLOW_PARAM, "Slow mode"); 151 configParam(FM_DEPTH_PARAM, 0.0f, 1.0f, 0.0f, "FM depth", "%", 0.0f, 100.0f); 152 configSwitch(FM_TYPE_PARAM, 0.0f, 1.0f, 1.0f, "FM mode", {"Linear FM", "Exponential FM"}); 153 configParam(SQUARE_PW_PARAM, -0.97f, 0.97f, 0.0f, "Square wave pulse width", "%", 0.0f, 100.0f); 154 configParam(SQUARE_PHASE_PARAM, -1.0f, 1.0f, 0.0f, "Square wave phase", "º", 0.0f, 180.0f); 155 configParam(SQUARE_MIX_PARAM, 0.0f, 1.0f, 1.0f, "Square wave mix", "%", 0.0f, 100.0f); 156 configParam(SAW_SATURATION_PARAM, 0.0f, 1.0f, 0.0f, "Saw wave saturation", "%", 0.0f, 100.0f); 157 configParam(SAW_PHASE_PARAM, -1.0f, 1.0f, 0.0f, "Saw wave phase", "º", 0.0f, 180.0f); 158 configParam(SAW_MIX_PARAM, 0.0f, 1.0f, 1.0f, "Saw wave mix", "%", 0.0f, 100.0f); 159 configParam(TRIANGLE_SAMPLE_PARAM, 0.0f, 1.0f, 0.0f, "Triangle wave sampling", "%", 0.0f, 100.0f); 160 configParam(TRIANGLE_PHASE_PARAM, -1.0f, 1.0f, 0.0f, "Triangle wave phase", "º", 0.0f, 180.0f); 161 configParam(TRIANGLE_MIX_PARAM, 0.0f, 1.0f, 1.0f, "Triangle wave mix", "%", 0.0f, 100.0f); 162 configParam(SINE_FEEDBACK_PARAM, 0.0f, 1.0f, 0.0f, "Sine wave feedback", "%", 0.0f, 100.0f); 163 configParam(SINE_PHASE_PARAM, -1.0f, 1.0f, 0.0f, "Sine wave phase", "º", 0.0f, 180.0f); 164 configParam(SINE_MIX_PARAM, 0.0f, 1.0f, 1.0f, "Sine wave mix", "%", 0.0f, 100.0f); 165 166 configInput(FM_INPUT, "Frequency modulation"); 167 configInput(FM_DEPTH_INPUT, "FM depth CV"); 168 configInput(SQUARE_PW_INPUT, "Square pulse width CV"); 169 configInput(SQUARE_PHASE_INPUT, "Square phase CV"); 170 configInput(SQUARE_MIX_INPUT, "Square mix CV"); 171 configInput(SAW_SATURATION_INPUT, "Saw saturation CV"); 172 configInput(SAW_PHASE_INPUT, "Saw phase CV"); 173 configInput(SAW_MIX_INPUT, "Saw mix CV"); 174 configInput(TRIANGLE_SAMPLE_INPUT, "Triangle sample CV"); 175 configInput(TRIANGLE_PHASE_INPUT, "Triangle phase CV"); 176 configInput(TRIANGLE_MIX_INPUT, "Triangle mix CV"); 177 configInput(SINE_FEEDBACK_INPUT, "Sine feedback CV"); 178 configInput(SINE_PHASE_INPUT, "Sine phase CV"); 179 configInput(SINE_MIX_INPUT, "Sine mix CV"); 180 configInput(PITCH_INPUT, "Pitch (1V/octave)"); 181 configInput(SYNC_INPUT, "Sync"); 182 183 configOutput(SQUARE_OUTPUT, "Square signal"); 184 configOutput(SAW_OUTPUT, "Saw signal"); 185 configOutput(TRIANGLE_OUTPUT, "Triangle signal"); 186 configOutput(SINE_OUTPUT, "Sine signal"); 187 configOutput(MIX_OUTPUT, "Mixed signal"); 188 } 189 190 void reset() override; 191 void sampleRateChange() override; 192 json_t* saveToJson(json_t* root) override; 193 void loadFromJson(json_t* root) override; 194 bool active() override; 195 int channels() override; 196 void addChannel(int c) override; 197 void removeChannel(int c) override; 198 void modulate() override; 199 void modulateChannel(int c) override; 200 void processChannel(const ProcessArgs& args, int c) override; 201 Phasor::phase_delta_t phaseOffset(int c, Param& param, Input& input); 202 float level(int c, Param& param, Input& input); 203 }; 204 205 } // namespace bogaudio