BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

Additator.cpp (10369B)


      1 
      2 #include "Additator.hpp"
      3 
      4 void Additator::Engine::reset() {
      5 	syncTrigger.reset();
      6 	phase = PHASE_RESET;
      7 }
      8 
      9 void Additator::Engine::sampleRateChange() {
     10 	float sampleRate = APP->engine->getSampleRate();
     11 	oscillator.setSampleRate(sampleRate);
     12 	maxFrequency = 0.475f * sampleRate;
     13 	phase = PHASE_RESET;
     14 	widthSL.setParams(sampleRate, slewLimitTime, maxWidth);
     15 	oddSkewSL.setParams(sampleRate, slewLimitTime, 2.0f * maxSkew);
     16 	evenSkewSL.setParams(sampleRate, slewLimitTime, 2.0f * maxSkew);
     17 	amplitudeNormalizationSL.setParams(sampleRate, slewLimitTime, maxAmplitudeNormalization - minAmplitudeNormalization);
     18 	decaySL.setParams(sampleRate, slewLimitTime, maxDecay - minDecay);
     19 	balanceSL.setParams(sampleRate, slewLimitTime, 2.0f);
     20 	filterSL.setParams(sampleRate, slewLimitTime, maxFilter - minFilter);
     21 }
     22 
     23 void Additator::reset() {
     24 	for (int c = 0; c < _channels; ++c) {
     25 		_engines[c]->reset();
     26 	}
     27 }
     28 
     29 void Additator::sampleRateChange() {
     30 	for (int c = 0; c < _channels; ++c) {
     31 		_engines[c]->sampleRateChange();
     32 	}
     33 }
     34 
     35 bool Additator::active() {
     36 	return outputs[AUDIO_OUTPUT].isConnected();
     37 }
     38 
     39 int Additator::channels() {
     40 	return inputs[PITCH_INPUT].getChannels();
     41 }
     42 
     43 void Additator::addChannel(int c) {
     44 	Engine& e = *(_engines[c] = new Engine());
     45 	e.reset();
     46 	e.sampleRateChange();
     47 
     48 	e.widthSL._last = widthParam(c);
     49 	e.oddSkewSL._last = oddSkewParam(c);
     50 	e.evenSkewSL._last = evenSkewParam(c);
     51 	e.amplitudeNormalizationSL._last = amplitudeNormalizationParam(c);
     52 	e.decaySL._last = decayParam(c);
     53 	e.balanceSL._last = balanceParam(c);
     54 	e.filterSL._last = filterParam(c);
     55 
     56 	modulateChannel(c);
     57 	if (c > 0) {
     58 		e.oscillator.syncTo(_engines[0]->oscillator);
     59 	}
     60 }
     61 
     62 void Additator::removeChannel(int c) {
     63 	delete _engines[c];
     64 	_engines[c] = NULL;
     65 }
     66 
     67 float Additator::widthParam(int c) {
     68 	return clamp(params[WIDTH_PARAM].getValue() + (maxWidth / 2.0f) * cvValue(c, inputs[WIDTH_INPUT]), 0.0f, maxWidth);
     69 }
     70 
     71 float Additator::oddSkewParam(int c) {
     72 	return clamp(params[ODD_SKEW_PARAM].getValue() + cvValue(c, inputs[ODD_SKEW_INPUT]), -maxSkew, maxSkew);
     73 }
     74 
     75 float Additator::evenSkewParam(int c) {
     76 	return clamp(params[EVEN_SKEW_PARAM].getValue() + cvValue(c, inputs[EVEN_SKEW_INPUT]), -maxSkew, maxSkew);
     77 }
     78 
     79 float Additator::amplitudeNormalizationParam(int c) {
     80 	return clamp(params[GAIN_PARAM].getValue() + ((maxAmplitudeNormalization - minAmplitudeNormalization) / 2.0f) * cvValue(c, inputs[GAIN_INPUT]), minAmplitudeNormalization, maxAmplitudeNormalization);
     81 }
     82 
     83 float Additator::decayParam(int c) {
     84 	return clamp(params[DECAY_PARAM].getValue() + ((maxDecay - minDecay) / 2.0f) * cvValue(c, inputs[DECAY_INPUT]), minDecay, maxDecay);
     85 }
     86 
     87 float Additator::balanceParam(int c) {
     88 	return clamp(params[BALANCE_PARAM].getValue() + cvValue(c, inputs[BALANCE_INPUT]), -1.0f, 1.0f);
     89 }
     90 
     91 float Additator::filterParam(int c) {
     92 	return clamp(params[FILTER_PARAM].getValue() + cvValue(c, inputs[FILTER_INPUT]), minFilter, maxFilter);
     93 }
     94 
     95 void Additator::modulateChannel(int c) {
     96 	Engine& e = *_engines[c];
     97 
     98 	float width = e.widthSL.next(widthParam(c));
     99 	float oddSkew = e.oddSkewSL.next(oddSkewParam(c));
    100 	float evenSkew = e.evenSkewSL.next(evenSkewParam(c));
    101 	if (
    102 		e.width != width ||
    103 		e.oddSkew != oddSkew ||
    104 		e.evenSkew != evenSkew
    105 	) {
    106 		e.width = width;
    107 		e.oddSkew = oddSkew;
    108 		e.evenSkew = evenSkew;
    109 
    110 		e.oscillator.setPartialFrequencyRatio(1, 1.0f);
    111 		e.activePartials = 1;
    112 		for (int i = 2, n = e.oscillator.partialCount(); i <= n; ++i) {
    113 			float ii = i;
    114 			if (i % 2 == 0) {
    115 				ii += e.evenSkew;
    116 			}
    117 			else {
    118 				ii += e.oddSkew;
    119 			}
    120 			if (e.oscillator.setPartialFrequencyRatio(i, powf(ii, e.width))) {
    121 				e.activePartials = i;
    122 			}
    123 		}
    124 	}
    125 
    126 	int partials = clamp((int)roundf(params[PARTIALS_PARAM].getValue() * cvValue(c, inputs[PARTIALS_INPUT], true)), 0, maxPartials);
    127 	float amplitudeNormalization = e.amplitudeNormalizationSL.next(amplitudeNormalizationParam(c));
    128 	float decay = e.decaySL.next(decayParam(c));
    129 	float balance = e.balanceSL.next(balanceParam(c));
    130 	float filter = e.filterSL.next(filterParam(c));
    131 	if (
    132 		e.partials != partials ||
    133 		e.amplitudeNormalization != amplitudeNormalization ||
    134 		e.decay != decay ||
    135 		e.balance != balance ||
    136 		e.filter != filter
    137 	) {
    138 		int envelopes = e.partials != partials ? std::max(e.partials, partials) : 0;
    139 		e.partials = partials;
    140 		e.amplitudeNormalization = amplitudeNormalization;
    141 		e.decay = decay;
    142 		e.balance = balance;
    143 		e.filter = filter;
    144 
    145 		float as[maxPartials + 1];
    146 		float total = as[1] = 1.0f;
    147 		filter = log10f(e.filter) + 1.0f;
    148 		int np = std::min(e.partials, e.activePartials);
    149 		for (int i = 2, n = e.oscillator.partialCount(); i <= n; ++i) {
    150 			as[i] = 0.0f;
    151 			if (i <= np) {
    152 				as[i] = powf(i, -e.decay) * powf(e.filter, i);
    153 				if (i % 2 == 0) {
    154 					if (e.balance > 0.0f) {
    155 						as[i] *= 1.0f - e.balance;
    156 					}
    157 				}
    158 				else {
    159 					if (e.balance < 0.0f) {
    160 						as[i] *= 1.0f + e.balance;
    161 					}
    162 				}
    163 				total += as[i];
    164 			}
    165 		}
    166 		float norm = std::max(np / (float)e.oscillator.partialCount(), 0.1f);
    167 		norm = 1.0f + (e.amplitudeNormalization - 1.0f) * norm;
    168 		norm = std::max(total / norm, 0.7f);
    169 		for (int i = 1, n = e.oscillator.partialCount(); i <= n; ++i) {
    170 			as[i] /= norm;
    171 			e.oscillator.setPartialAmplitude(i, as[i], i <= envelopes);
    172 		}
    173 	}
    174 
    175 	float frequency = params[FREQUENCY_PARAM].getValue();
    176 	frequency += params[FINE_PARAM].getValue() / 12.0f;
    177 	if (inputs[PITCH_INPUT].isConnected()) {
    178 		frequency += clamp(inputs[PITCH_INPUT].getVoltage(c), -5.0f, 5.0f);
    179 	}
    180 	frequency = clamp(cvToFrequency(frequency), 20.0f, e.maxFrequency);
    181 	e.oscillator.setFrequency(frequency);
    182 
    183 	Phase phase = params[PHASE_PARAM].getValue() > 1.5f ? PHASE_COSINE : PHASE_SINE;
    184 	if (e.phase != phase) {
    185 		e.phase = phase;
    186 		e.oscillator.syncToPhase(e.phase == PHASE_SINE ? 0.0f : M_PI / 2.0f);
    187 	}
    188 }
    189 
    190 void Additator::processAlways(const ProcessArgs& args) {
    191 	Phase phase = params[PHASE_PARAM].getValue() > 1.5f ? PHASE_COSINE : PHASE_SINE;
    192 	lights[SINE_LIGHT].value = phase == PHASE_SINE;
    193 	lights[COSINE_LIGHT].value = phase == PHASE_COSINE;
    194 }
    195 
    196 void Additator::processChannel(const ProcessArgs& args, int c) {
    197 	Engine& e = *_engines[c];
    198 
    199 	if (e.syncTrigger.next(inputs[SYNC_INPUT].getPolyVoltage(c))) {
    200 		e.oscillator.syncToPhase(e.phase == PHASE_SINE ? 0.0f : M_PI / 2.0f);
    201 	}
    202 	outputs[AUDIO_OUTPUT].setChannels(_channels);
    203 	outputs[AUDIO_OUTPUT].setVoltage(e.oscillator.next() * 5.0, c);
    204 }
    205 
    206 float Additator::cvValue(int c, Input& cv, bool dc) {
    207 	if (!cv.isConnected()) {
    208 		return dc ? 1.0f : 0.0f;
    209 	}
    210 	if (dc) {
    211 		return clamp(cv.getPolyVoltage(c) / 10.0f, 0.0f, 1.0f);
    212 	}
    213 	return clamp(cv.getPolyVoltage(c) / 5.0f, -1.0f, 1.0f);
    214 }
    215 
    216 struct AdditatorWidget : BGModuleWidget {
    217 	static constexpr int hp = 15;
    218 
    219 	AdditatorWidget(Additator* module) {
    220 		setModule(module);
    221 		box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
    222 		setPanel(box.size, "Additator");
    223 		createScrews();
    224 
    225 		// generated by svg_widgets.rb
    226 		auto frequencyParamPosition = Vec(40.0, 45.0);
    227 		auto partialsParamPosition = Vec(165.0, 60.0);
    228 		auto fineParamPosition = Vec(30.0, 160.0);
    229 		auto widthParamPosition = Vec(79.0, 155.0);
    230 		auto oddSkewParamPosition = Vec(132.0, 155.0);
    231 		auto evenSkewParamPosition = Vec(184.0, 155.0);
    232 		auto gainParamPosition = Vec(25.0, 218.0);
    233 		auto decayParamPosition = Vec(79.0, 218.0);
    234 		auto balanceParamPosition = Vec(132.0, 218.0);
    235 		auto filterParamPosition = Vec(184.0, 218.0);
    236 		auto phaseParamPosition = Vec(194.0, 299.0);
    237 
    238 		auto syncInputPosition = Vec(16.0, 274.0);
    239 		auto partialsInputPosition = Vec(50.0, 274.0);
    240 		auto widthInputPosition = Vec(84.0, 274.0);
    241 		auto oddSkewInputPosition = Vec(118.0, 274.0);
    242 		auto evenSkewInputPosition = Vec(152.0, 274.0);
    243 		auto pitchInputPosition = Vec(16.0, 318.0);
    244 		auto gainInputPosition = Vec(50.0, 318.0);
    245 		auto decayInputPosition = Vec(84.0, 318.0);
    246 		auto balanceInputPosition = Vec(118.0, 318.0);
    247 		auto filterInputPosition = Vec(152.0, 318.0);
    248 
    249 		auto audioOutputPosition = Vec(186.0, 318.0);
    250 
    251 		auto sineLightPosition = Vec(185.0, 272.0);
    252 		auto cosineLightPosition = Vec(185.0, 287.0);
    253 		// end generated by svg_widgets.rb
    254 
    255 		addParam(createParam<Knob68>(frequencyParamPosition, module, Additator::FREQUENCY_PARAM));
    256 		addParam(createParam<Knob38>(partialsParamPosition, module, Additator::PARTIALS_PARAM));
    257 		addParam(createParam<Knob16>(fineParamPosition, module, Additator::FINE_PARAM));
    258 		addParam(createParam<Knob26>(widthParamPosition, module, Additator::WIDTH_PARAM));
    259 		addParam(createParam<Knob26>(oddSkewParamPosition, module, Additator::ODD_SKEW_PARAM));
    260 		addParam(createParam<Knob26>(evenSkewParamPosition, module, Additator::EVEN_SKEW_PARAM));
    261 		addParam(createParam<Knob26>(gainParamPosition, module, Additator::GAIN_PARAM));
    262 		addParam(createParam<Knob26>(decayParamPosition, module, Additator::DECAY_PARAM));
    263 		addParam(createParam<Knob26>(balanceParamPosition, module, Additator::BALANCE_PARAM));
    264 		addParam(createParam<Knob26>(filterParamPosition, module, Additator::FILTER_PARAM));
    265 		addParam(createParam<StatefulButton9>(phaseParamPosition, module, Additator::PHASE_PARAM));
    266 
    267 		addInput(createInput<Port24>(partialsInputPosition, module, Additator::PARTIALS_INPUT));
    268 		addInput(createInput<Port24>(widthInputPosition, module, Additator::WIDTH_INPUT));
    269 		addInput(createInput<Port24>(oddSkewInputPosition, module, Additator::ODD_SKEW_INPUT));
    270 		addInput(createInput<Port24>(evenSkewInputPosition, module, Additator::EVEN_SKEW_INPUT));
    271 		addInput(createInput<Port24>(gainInputPosition, module, Additator::GAIN_INPUT));
    272 		addInput(createInput<Port24>(decayInputPosition, module, Additator::DECAY_INPUT));
    273 		addInput(createInput<Port24>(balanceInputPosition, module, Additator::BALANCE_INPUT));
    274 		addInput(createInput<Port24>(filterInputPosition, module, Additator::FILTER_INPUT));
    275 		addInput(createInput<Port24>(pitchInputPosition, module, Additator::PITCH_INPUT));
    276 		addInput(createInput<Port24>(syncInputPosition, module, Additator::SYNC_INPUT));
    277 
    278 		addOutput(createOutput<Port24>(audioOutputPosition, module, Additator::AUDIO_OUTPUT));
    279 
    280 		addChild(createLight<BGSmallLight<GreenLight>>(sineLightPosition, module, Additator::SINE_LIGHT));
    281 		addChild(createLight<BGSmallLight<GreenLight>>(cosineLightPosition, module, Additator::COSINE_LIGHT));
    282 	}
    283 };
    284 
    285 Model* modelAdditator = bogaudio::createModel<Additator, AdditatorWidget>("Bogaudio-Additator", "ADDITATOR", "Additive oscillator", "Oscillator", "Polyphonic");