#include #include #include "Senior.hpp" namespace Ottobit { #define BITS 16 #define RATE 64 void Senior::prepare(setup info) { // Initialization, this method will be called with buffer size, etc. set_bit_factor(); lfo.set_sample_rate(info.rate); lfo.freq(5); skipped_frames.resize(info.output_channels); fms.resize(info.output_channels); lpfs.resize(info.output_channels); phases.resize(info.output_channels); float init_phase{0}; const float phase_frac{1.f / info.output_channels}; for (auto [fm, lpf, ph] : std::views::zip(fms, lpfs, phases)) { fm.set_sample_rate(info.rate); fm.maxDelay(0.1); lpf.set_sample_rate(info.rate); lpf.type(gam::LOW_PASS); ph = init_phase; init_phase += phase_frac; } } void Senior::operator()(tick t) { // Only compute skipped_frames when control changes if (inputs.s_rate != previous_rate) { step_size = (1. - inputs.s_rate) * RATE; previous_rate = inputs.s_rate; } if (inputs.bits != previous_bits) { set_bit_factor(); previous_bits = inputs.bits; } if (inputs.depth != previous_depth || inputs.am_fm != previous_am_fm) { if (inputs.am_fm < 1.f) am_amp = (inputs.am_fm * 0.5) + (inputs.depth * 0.5); else { am_amp = 2 - inputs.am_fm; fm_amp = inputs.am_fm - 1; } previous_depth = inputs.depth; previous_am_fm = inputs.am_fm; } // Process the input buffer for each ouput channels for(int i{0}; i < outputs.audio.channels; i++) { auto* out{outputs.audio[i]}; auto* in{inputs.audio[i % inputs.audio.channels]}; // Init current_frame and out_frame for each channel double current_frame{crush(in[0])}; double out_frame{current_frame}; for(int j{0}; j < t.frames; j++) { if (skipped_frames[i] <= step_size) skipped_frames[i]++; else { skipped_frames[i] = 0; current_frame = crush(in[j]); } if (inputs.depth) { lfo.set(inputs.freq, phases[i], 0); if (inputs.am_fm < 1.f) out_frame = current_frame * (lfo.cos() * am_amp + (1 - am_amp)); else { double amp = lfo.cos() * inputs.depth; fms[i].delay(amp * .0025 + 1); double am = current_frame * (amp + 1 - inputs.depth); double fm = fms[i](current_frame); out_frame = am * am_amp + fm * fm_amp; } phases[i] = lfo.phase(); } else out_frame = current_frame; if (inputs.filter < 1.f) { lpfs[i].freq(inputs.filter * 3000.); out[j] = lpfs[i](out_frame); } else out[j] = out_frame; } } } void Senior::set_bit_factor() { bit_factor = pow(2, inputs.bits * BITS) - 1; } double Senior::crush(const double& f) { if (inputs.bits < 1.f) // Rounding using static_cast // return static_cast(static_cast(f * bit_factor)) / bit_factor; // Close, but prefer the use of roundMagic from Lance Putnam's Gama return gam::scl::round(f * bit_factor) / bit_factor; else return f; } } // namespace Ottobit