BogaudioModules

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

commit 572f6b29a84e94a18523d5353cbddb9b2b0a2c4b
parent 65a4badf9bae4ce7347cdb6e4b680c9c63d167f8
Author: Matt Demanett <matt@demanett.net>
Date:   Sat, 23 Jan 2021 00:33:26 -0500

FM-OP: trigger anti-aliasing when there is external FM; add options to disable AA for feedback or external FM.

Diffstat:
MREADME-prerelease.md | 2++
Msrc/FMOp.cpp | 21++++++++++++++++++++-
Msrc/FMOp.hpp | 2++
3 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/README-prerelease.md b/README-prerelease.md @@ -138,6 +138,8 @@ A sine-wave oscillator and simple synth voice designed to allow patching up the - An on-board ADSR, controlled by the GATE input, with selectable routing to output level, feedback and depth, with CV control over the sustain level. - A main frequency knob calibrated for setting the frequency as a ratio of the frequency dictated by the V/OCT input - assuming a single V/OCT CV is routed to multiple FM-OPs, this allows the relative frequency of each operator to be set via ratios. +Anti-aliasing techniques are applied when feedback or external FM are in use. Either condition for anti-aliasing can be disabled on the context menu. Prior to version 1.1.36, due to a long-standing bug, there was no anti-aliasing for external FM, unless feedback was also on. To get that behavior back, **the true vintage FM-OP sound**, disable "Anti-alias external FM" on the menu. + _Polyphony:_ <a href="#polyphony">Polyphonic</a>, with channels defined by the V/OCT input. #### <a name="chirp"></a> CHIRP diff --git a/src/FMOp.cpp b/src/FMOp.cpp @@ -3,6 +3,8 @@ #include "dsp/pitch.hpp" #define LINEAR_LEVEL "linearLevel" +#define ANTIALIAS_FEEDBACK "antialias_feedback" +#define ANTIALIAS_DEPTH "antialias_depth" float FMOp::RatioParamQuantity::getDisplayValue() { float v = getValue(); @@ -56,6 +58,8 @@ void FMOp::Engine::sampleRateChange() { json_t* FMOp::toJson(json_t* root) { json_object_set_new(root, LINEAR_LEVEL, json_boolean(_linearLevel)); + json_object_set_new(root, ANTIALIAS_FEEDBACK, json_boolean(_antiAliasFeedback)); + json_object_set_new(root, ANTIALIAS_DEPTH, json_boolean(_antiAliasDepth)); return root; } @@ -64,6 +68,16 @@ void FMOp::fromJson(json_t* root) { if (ll) { _linearLevel = json_is_true(ll); } + + json_t* aaf = json_object_get(root, ANTIALIAS_FEEDBACK); + if (aaf) { + _antiAliasFeedback = json_is_true(aaf); + } + + json_t* aad = json_object_get(root, ANTIALIAS_DEPTH); + if (aad) { + _antiAliasDepth = json_is_true(aad); + } } void FMOp::reset() { @@ -196,18 +210,20 @@ void FMOp::processChannel(const ProcessArgs& args, int c) { offset = feedback * e.feedbackDelayedSample; } + bool depthOn = false; if (inputs[FM_INPUT].isConnected()) { float depth = e.depthSL.next(e.depth); if (_depthEnvelopeOn) { depth *= envelope; } offset += inputs[FM_INPUT].getPolyVoltage(c) * depth * 2.0f; + depthOn = depth > 0.001f; } float sample = 0.0f; if (out > 0.0001f) { Phasor::phase_delta_t o = Phasor::radiansToPhase(offset); - if (feedbackOn) { + if ((feedbackOn && _antiAliasFeedback) || (depthOn && _antiAliasDepth)) { if (e.oversampleMix < 1.0f) { e.oversampleMix += oversampleMixIncrement; } @@ -331,6 +347,9 @@ struct FMOpWidget : BGModuleWidget { auto fmop = dynamic_cast<FMOp*>(module); assert(fmop); menu->addChild(new BoolOptionMenuItem("Linear level response", [fmop]() { return &fmop->_linearLevel; })); + + menu->addChild(new BoolOptionMenuItem("Anti-alias feedback", [fmop]() { return &fmop->_antiAliasFeedback; })); + menu->addChild(new BoolOptionMenuItem("Anti-alias external FM", [fmop]() { return &fmop->_antiAliasDepth; })); } }; diff --git a/src/FMOp.hpp b/src/FMOp.hpp @@ -84,6 +84,8 @@ struct FMOp : BGModule { }; bool _linearLevel = false; + bool _antiAliasFeedback = true; + bool _antiAliasDepth = true; bool _levelEnvelopeOn = false; bool _feedbackEnvelopeOn = false; bool _depthEnvelopeOn = false;