vco_base.cpp (5934B)
1 #include "vco_base.hpp" 2 #include "dsp/pitch.hpp" 3 4 #define POLY_INPUT "poly_input" 5 #define DC_CORRECTION "dc_correction" 6 7 float VCOBase::VCOFrequencyParamQuantity::offset() { 8 auto vco = dynamic_cast<VCOBase*>(module); 9 return vco->_slowMode ? vco->slowModeOffset : 0.0f; 10 } 11 12 float VCOBase::VCOFrequencyParamQuantity::getDisplayValue() { 13 float v = getValue(); 14 if (!module) { 15 return v; 16 } 17 18 auto vco = dynamic_cast<VCOBase*>(module); 19 return vco->_linearMode ? (vco->_slowMode ? v : v * 1000.0f) : FrequencyParamQuantity::getDisplayValue(); 20 } 21 22 void VCOBase::VCOFrequencyParamQuantity::setDisplayValue(float v) { 23 if (!module) { 24 return; 25 } 26 27 auto vco = dynamic_cast<VCOBase*>(module); 28 if (vco->_linearMode) { 29 if (vco->_slowMode) { 30 setValue(v / 1000.0f); 31 } 32 else { 33 setValue(v); 34 } 35 } 36 else { 37 FrequencyParamQuantity::setDisplayValue(v); 38 } 39 } 40 41 void VCOBase::Engine::reset() { 42 syncTrigger.reset(); 43 } 44 45 void VCOBase::Engine::sampleRateChange(float sampleRate) { 46 phasor.setSampleRate(sampleRate); 47 square.setSampleRate(sampleRate); 48 saw.setSampleRate(sampleRate); 49 squareDecimator.setParams(sampleRate, oversample); 50 sawDecimator.setParams(sampleRate, oversample); 51 triangleDecimator.setParams(sampleRate, oversample); 52 squarePulseWidthSL.setParams(sampleRate, 0.1f, 2.0f); 53 } 54 55 void VCOBase::Engine::setFrequency(float f) { 56 if (frequency != f && f < 0.475f * phasor._sampleRate) { 57 frequency = f; 58 phasor.setFrequency(frequency / (float)oversample); 59 square.setFrequency(frequency); 60 saw.setFrequency(frequency); 61 } 62 } 63 64 void VCOBase::reset() { 65 for (int c = 0; c < _channels; ++c) { 66 _engines[c]->reset(); 67 } 68 } 69 70 void VCOBase::sampleRateChange() { 71 float sampleRate = APP->engine->getSampleRate(); 72 _oversampleThreshold = 0.06f * sampleRate; 73 74 for (int c = 0; c < _channels; ++c) { 75 _engines[c]->sampleRateChange(sampleRate); 76 } 77 } 78 79 json_t* VCOBase::saveToJson(json_t* root) { 80 json_object_set_new(root, POLY_INPUT, json_integer(_polyInputID)); 81 json_object_set_new(root, DC_CORRECTION, json_boolean(_dcCorrection)); 82 return root; 83 } 84 85 void VCOBase::loadFromJson(json_t* root) { 86 json_t* p = json_object_get(root, POLY_INPUT); 87 if (p) { 88 _polyInputID = json_integer_value(p); 89 } 90 91 json_t* dc = json_object_get(root, DC_CORRECTION); 92 if (dc) { 93 _dcCorrection = json_boolean_value(dc); 94 } 95 } 96 97 int VCOBase::channels() { 98 return _polyInputID == _fmInputID ? inputs[_fmInputID].getChannels() : inputs[_pitchInputID].getChannels(); 99 } 100 101 void VCOBase::addChannel(int c) { 102 _engines[c] = new Engine(); 103 _engines[c]->reset(); 104 _engines[c]->sampleRateChange(APP->engine->getSampleRate()); 105 if (c > 0) { 106 _engines[c]->phasor.syncPhase(_engines[0]->phasor); 107 } 108 } 109 110 void VCOBase::removeChannel(int c) { 111 delete _engines[c]; 112 _engines[c] = NULL; 113 } 114 115 void VCOBase::modulateChannel(int c) { 116 Engine& e = *_engines[c]; 117 118 e.baseVOct = params[_frequencyParamID].getValue(); 119 if (_fineFrequencyParamID >= 0) { 120 e.baseVOct += params[_fineFrequencyParamID].getValue() / 12.0f; 121 } 122 if (inputs[_pitchInputID].isConnected()) { 123 e.baseVOct += clamp(inputs[_pitchInputID].getVoltage(c), -5.0f, 5.0f); 124 } 125 if (_linearMode) { 126 e.baseHz = linearModeVoltsToHertz(e.baseVOct); 127 } 128 else { 129 if (_slowMode) { 130 e.baseVOct += slowModeOffset; 131 } 132 e.baseHz = cvToFrequency(e.baseVOct); 133 } 134 } 135 136 void VCOBase::processChannel(const ProcessArgs& args, int c) { 137 Engine& e = *_engines[c]; 138 139 if (e.syncTrigger.next(inputs[_syncInputID].getPolyVoltage(c))) { 140 e.phasor.resetPhase(); 141 } 142 143 float frequency = e.baseHz; 144 Phasor::phase_delta_t phaseOffset = 0; 145 if (_fmInputID >= 0 && inputs[_fmInputID].isConnected() && _fmDepth > 0.01f) { 146 float fm = inputs[_fmInputID].getPolyVoltage(c) * _fmDepth; 147 if (_fmLinearMode) { 148 phaseOffset = Phasor::radiansToPhase(2.0f * fm); 149 } 150 else if (_linearMode) { 151 frequency += linearModeVoltsToHertz(fm); 152 } 153 else { 154 frequency = cvToFrequency(e.baseVOct + fm); 155 } 156 } 157 e.setFrequency(frequency); 158 159 const float oversampleWidth = 100.0f; 160 float mix, oMix; 161 if (frequency > _oversampleThreshold) { 162 if (frequency > _oversampleThreshold + oversampleWidth) { 163 mix = 0.0f; 164 oMix = 1.0f; 165 } 166 else { 167 oMix = (frequency - _oversampleThreshold) / oversampleWidth; 168 mix = 1.0f - oMix; 169 } 170 } 171 else { 172 mix = 1.0f; 173 oMix = 0.0f; 174 } 175 176 e.squareOut = 0.0f; 177 e.sawOut = 0.0f; 178 e.triangleOut = 0.0f; 179 if (oMix > 0.0f) { 180 for (int i = 0; i < Engine::oversample; ++i) { 181 e.phasor.advancePhase(); 182 if (e.squareActive) { 183 e.squareBuffer[i] = e.square.nextFromPhasor(e.phasor, phaseOffset + e.additionalPhaseOffset); 184 } 185 if (e.sawActive) { 186 e.sawBuffer[i] = e.saw.nextFromPhasor(e.phasor, phaseOffset + e.additionalPhaseOffset); 187 } 188 if (e.triangleActive) { 189 e.triangleBuffer[i] = e.triangle.nextFromPhasor(e.phasor, phaseOffset + e.additionalPhaseOffset); 190 } 191 } 192 if (e.squareActive) { 193 e.squareOut += oMix * amplitude * e.squareDecimator.next(e.squareBuffer); 194 } 195 if (e.sawActive) { 196 e.sawOut += oMix * amplitude * e.sawDecimator.next(e.sawBuffer); 197 } 198 if (e.triangleActive) { 199 e.triangleOut += oMix * amplitude * e.triangleDecimator.next(e.triangleBuffer); 200 } 201 } 202 else { 203 e.phasor.advancePhase(Engine::oversample); 204 } 205 if (mix > 0.0f) { 206 if (e.squareActive) { 207 e.squareOut += mix * amplitude * e.square.nextFromPhasor(e.phasor, phaseOffset + e.additionalPhaseOffset); 208 } 209 if (e.sawActive) { 210 e.sawOut += mix * amplitude * e.saw.nextFromPhasor(e.phasor, phaseOffset + e.additionalPhaseOffset); 211 } 212 if (e.triangleActive) { 213 e.triangleOut += mix * amplitude * e.triangle.nextFromPhasor(e.phasor, phaseOffset + e.additionalPhaseOffset); 214 } 215 } 216 217 e.sineOut = e.sineActive ? (amplitude * e.sine.nextFromPhasor(e.phasor, phaseOffset + e.additionalPhaseOffset)) : 0.0f; 218 } 219 220 221 void VCOBaseModuleWidget::contextMenu(Menu* menu) { 222 auto m = dynamic_cast<VCOBase*>(module); 223 assert(m); 224 menu->addChild(new BoolOptionMenuItem("DC offset correction", [m]() { return &m->_dcCorrection; })); 225 }