/* * Brickworks * * Copyright (C) 2022-2024 Orastron Srl unipersonale * * Brickworks 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, version 3 of the License. * * Brickworks 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. * * You should have received a copy of the GNU General Public License * along with Brickworks. If not, see . * * File author: Stefano D'Angelo */ #include "impl.h" #include "common.h" #include #include #include #include #include #include #include #include #define BUFFER_SIZE 128 using namespace Brickworks; class Engine { public: PhaseGen<1> phaseGen; OscPulse<1> oscPulse; OscFilt<1> oscFilt; SVF<1> svf; EnvGen<1> envGen; Gain<1> gain; PPM<1> ppm; float masterTune; int note; float buf[BUFFER_SIZE]; }; extern "C" { impl impl_new(void) { Engine *instance = new Engine(); instance->oscPulse.setAntialiasing(true); return reinterpret_cast(instance); } void impl_free(impl handle) { Engine *instance = reinterpret_cast(handle); delete instance; } void impl_set_sample_rate(impl handle, float sample_rate) { Engine *instance = reinterpret_cast(handle); instance->phaseGen.setSampleRate(sample_rate); instance->oscPulse.setSampleRate(sample_rate); instance->svf.setSampleRate(sample_rate); instance->envGen.setSampleRate(sample_rate); instance->gain.setSampleRate(sample_rate); instance->ppm.setSampleRate(sample_rate); } void impl_reset(impl handle) { Engine *instance = reinterpret_cast(handle); instance->phaseGen.reset(); instance->oscPulse.reset(); instance->oscFilt.reset(); instance->svf.reset(); instance->envGen.reset(); instance->gain.reset(); instance->ppm.reset(); instance->note = -1; } void impl_set_parameter(impl handle, size_t index, float value) { Engine *instance = reinterpret_cast(handle); switch (index) { case plugin_parameter_volume: { float v = 0.01f * value; instance->gain.setGainLin(v * v * v); } break; case plugin_parameter_master_tune: instance->masterTune = value; break; case plugin_parameter_portamento: // using portamento time 0% -> 90%: tau = portamento time / log(10) instance->phaseGen.setPortamentoTau((0.001f * 0.4342944819032517f) * value); break; case plugin_parameter_pulse_width: instance->oscPulse.setPulseWidth(0.01f * value); break; case plugin_parameter_cutoff: instance->svf.setCutoff(value); break; case plugin_parameter_resonance: instance->svf.setQ(0.5f + (9.5f * 0.01f) * value); break; case plugin_parameter_attack: instance->envGen.setAttack(0.001f * value); break; case plugin_parameter_decay: instance->envGen.setDecay(0.001f * value); break; case plugin_parameter_sustain: instance->envGen.setSustain(0.01f * value); break; case plugin_parameter_release: instance->envGen.setRelease(0.001f * value); break; } } float impl_get_parameter(impl handle, size_t index) { (void)index; Engine *instance = reinterpret_cast(handle); return bw_clipf(instance->ppm.getYZ1(0), -60.f, 0.f); } void impl_process(impl handle, const float **inputs, float **outputs, size_t n_samples) { (void)inputs; Engine *instance = reinterpret_cast(handle); if (instance->note >= 0) instance->phaseGen.setFrequency(instance->masterTune * bw_pow2f(8.333333333333333e-2f * (instance->note - 69))); for (size_t i = 0; i < n_samples; i += BUFFER_SIZE) { float *out = outputs[0] + i; size_t ni = n_samples - i; size_t n = ni < BUFFER_SIZE ? ni : BUFFER_SIZE; #ifdef WASM float *y[1] = {out}; float *b[1] = {instance->buf}; char gate[1] = {instance->note >= 0}; instance->phaseGen.process(nullptr, y, b, n); instance->oscPulse.process(y, b, y, n); instance->oscFilt.process(y, y, n); instance->svf.process(y, y, nullptr, nullptr, n); instance->envGen.process(gate, b, n); bufMul<1>(y, b, y, n); instance->gain.process(y, y, n); instance->ppm.process(y, nullptr, n); #else instance->phaseGen.process({nullptr}, {out}, {instance->buf}, n); instance->oscPulse.process({out}, {instance->buf}, {out}, n); instance->oscFilt.process({out}, {out}, n); instance->svf.process({out}, {out}, {nullptr}, {nullptr}, n); instance->envGen.process({instance->note >= 0}, {instance->buf}, n); bufMul<1>({out}, {instance->buf}, {out}, n); instance->gain.process({out}, {out}, n); instance->ppm.process({out}, {nullptr}, n); #endif } } void impl_midi_msg_in(impl handle, size_t index, const uint8_t * data) { (void)index; Engine *instance = reinterpret_cast(handle); switch (data[0] & 0xf0) { case 0x90: // note on if (data[2] == 0) { // no, note off actually if (data[1] == instance->note) instance->note = -1; } else instance->note = data[1]; break; case 0x80: // note off if (data[1] == instance->note) instance->note = -1; break; } } }