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:
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;