From e3b5ceebfcec304e9a1d3c89dc358df78047e58e Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 2 Jul 2013 22:38:09 -0700 Subject: [PATCH] Implement overdrive in resonant filter This patch implements nonlinear distortion in the resonant ladder filter. It's based on the differential equations in the Huovilainen '04 DAFx paper, but using matrix exponential to compute the evolution of the state variables. The implementation is scalar (and calls into sqrtf), but designed to be implemented in very efficient SIMD. --- android/res/layout/piano2.xml | 12 ++++- android/res/values/strings.xml | 4 ++ .../android/ui/PianoActivity2.java | 12 ++++- cpp/src/resofilter.cc | 50 +++++++++++++++---- cpp/src/synth_unit.cc | 5 ++ cpp/src/synth_unit.h | 2 +- 6 files changed, 71 insertions(+), 14 deletions(-) diff --git a/android/res/layout/piano2.xml b/android/res/layout/piano2.xml index 83e7b4a..90571ea 100644 --- a/android/res/layout/piano2.xml +++ b/android/res/layout/piano2.xml @@ -19,7 +19,9 @@ android:id="@+id/resonanceLabel" android:gravity="center_horizontal" /> + + android:layout_span="3" > Cutoff Resonance + Overdrive Attack @@ -79,4 +80,7 @@ Capture + + Settings + diff --git a/android/src/com/levien/synthesizer/android/ui/PianoActivity2.java b/android/src/com/levien/synthesizer/android/ui/PianoActivity2.java index 12e5d4e..c62efef 100644 --- a/android/src/com/levien/synthesizer/android/ui/PianoActivity2.java +++ b/android/src/com/levien/synthesizer/android/ui/PianoActivity2.java @@ -68,6 +68,7 @@ public class PianoActivity2 extends Activity { piano_ = (PianoView)findViewById(R.id.piano); cutoffKnob_ = (KnobView)findViewById(R.id.cutoffKnob); resonanceKnob_ = (KnobView)findViewById(R.id.resonanceKnob); + overdriveKnob_ = (KnobView)findViewById(R.id.overdriveKnob); presetSpinner_ = (Spinner)findViewById(R.id.presetSpinner); AudioParams params = new AudioParams(44100, 64); @@ -108,13 +109,19 @@ public class PianoActivity2 extends Activity { public void onKnobChanged(double newValue) { int value = (int)Math.round(newValue * 127); androidGlue_.onController(0, 1, value); - } + } }); resonanceKnob_.setKnobListener(new KnobListener() { public void onKnobChanged(double newValue) { int value = (int)Math.round(newValue * 127); androidGlue_.onController(0, 2, value); - } + } + }); + overdriveKnob_.setKnobListener(new KnobListener() { + public void onKnobChanged(double newValue) { + int value = (int)Math.round(newValue * 127); + androidGlue_.onController(0, 3, value); + } }); piano_.bindTo(androidGlue_); @@ -289,6 +296,7 @@ public class PianoActivity2 extends Activity { private PianoView piano_; private KnobView cutoffKnob_; private KnobView resonanceKnob_; + private KnobView overdriveKnob_; private Spinner presetSpinner_; private Handler statusHandler_; private Runnable statusRunnable_; diff --git a/cpp/src/resofilter.cc b/cpp/src/resofilter.cc index 431aff1..43f50af 100644 --- a/cpp/src/resofilter.cc +++ b/cpp/src/resofilter.cc @@ -23,10 +23,12 @@ #include #include +#include #include "synth.h" #include "freqlut.h" #include "exp2.h" +#include "aligned_buf.h" #include "resofilter.h" double this_sample_rate; @@ -173,19 +175,49 @@ void test_matrix() { } #if defined(USE_MATRIX) +static float sigmoid(float x, float overdrive) { + float xs = overdrive * x * (1.0 / (1 << 24)); + float isq = 1.0 / sqrtf(1 + xs * xs); + return x * isq; +} + void ResoFilter::process(const int32_t **inbufs, const int32_t *control_in, const int32_t *control_last, int32_t **outbufs) { - float a[20]; - make_state_transition(a, compute_alpha(control_in[0]), control_in[1]); + AlignedBuf a; + make_state_transition(a.get(), compute_alpha(control_in[0]), control_in[1]); + float overdrive = control_in[2] * (1.0 / (1 << 24)); const int32_t *ibuf = inbufs[0]; int32_t *obuf = outbufs[0]; - for (int i = 0; i < n; i++) { - float signal = ibuf[i]; - float tmp[4]; - matvec4(tmp, a + 4, x); - for (int k = 0; k < 4; k++) { - x[k] = tmp[k] + signal * a[k]; - obuf[i] = x[3]; + if (overdrive == 0) { + for (int i = 0; i < n; i++) { + float signal = ibuf[i]; + float tmp[4]; + matvec4(tmp, a.get() + 4, x); + for (int k = 0; k < 4; k++) { + x[k] = tmp[k] + signal * a.get()[k]; + obuf[i] = x[3]; + } + } + } else { + float ogain = 1 + overdrive; + float k = control_in[1] * (1.0 / (1<<24)); + for (int i = 0; i < 4; i++) { + a.get()[4 + 5 * i] -= 1.0; + a.get()[16 + i] += k * a.get()[i]; + } + for (int i = 0; i < n; i++) { + float signal = ibuf[i]; + float tmp[4]; + float tx[4]; + for (int j = 0; j < 4; j++) { + tx[j] = sigmoid(x[j], overdrive); + } + matvec4(tmp, a.get() + 4, tx); + float xin = sigmoid(signal - k * x[3], overdrive); + for (int j = 0; j < 4; j++) { + x[j] += tmp[j] + xin * a.get()[j]; + obuf[i] = x[3] * ogain; + } } } } diff --git a/cpp/src/synth_unit.cc b/cpp/src/synth_unit.cc index 546766a..9a69f5d 100644 --- a/cpp/src/synth_unit.cc +++ b/cpp/src/synth_unit.cc @@ -49,6 +49,7 @@ char epiano[] = { void SynthUnit::Init(double sample_rate) { Freqlut::init(sample_rate); Exp2::init(); + Tanh::init(); Sin::init(); Lfo::init(sample_rate); PitchEnv::init(sample_rate); @@ -68,6 +69,7 @@ SynthUnit::SynthUnit(RingBuffer *ring_buffer) { current_note_ = 0; filter_control_[0] = 258847126; filter_control_[1] = 0; + filter_control_[2] = 0; controllers_.values_[kControllerPitch] = 0x2000; sustain_ = false; extra_buf_size_ = 0; @@ -121,6 +123,7 @@ void SynthUnit::SetController(int controller, int value) { int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) { uint8_t cmd = buf[0]; uint8_t cmd_type = cmd & 0xf0; + //LOGI("got %d midi: %02x %02x %02x", buf_size, buf[0], buf[1], buf[2]); if (cmd_type == 0x80 || (cmd_type == 0x90 && buf[2] == 0)) { if (buf_size >= 3) { // note off @@ -163,6 +166,8 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) { filter_control_[0] = 142365917 + value * 917175; } else if (controller == 2) { filter_control_[1] = value * 528416; + } else if (controller == 3) { + filter_control_[2] = value * 528416; } else if (controller == 64) { sustain_ = value != 0; if (!sustain_) { diff --git a/cpp/src/synth_unit.h b/cpp/src/synth_unit.h index 597345f..1dd6777 100644 --- a/cpp/src/synth_unit.h +++ b/cpp/src/synth_unit.h @@ -70,7 +70,7 @@ class SynthUnit { Controllers controllers_; ResoFilter filter_; - int32_t filter_control_[2]; + int32_t filter_control_[3]; bool sustain_; // Extra buffering for when GetSamples wants a buffer not a multiple of N