@@ -3,6 +3,11 @@ | |||
// By Richard van Hoesel | |||
// https://forum.pjrc.com/threads/60488?p=269756&viewfull=1#post269756 | |||
// Ladder filter demo with continuous 3-saw drone into the filter | |||
// with separate LFOs modulating filter frequency and resonance. | |||
// By Richard van Hoesel | |||
// https://forum.pjrc.com/threads/60488?p=269756&viewfull=1#post269756 | |||
#include <Audio.h> | |||
AudioSynthWaveform waveform1; | |||
@@ -32,8 +37,9 @@ void setup() { | |||
sgtl5000_1.enable(); | |||
sgtl5000_1.volume(0.6); | |||
filter1.resonance(0.55); | |||
filter1.frequency(4000); | |||
filter1.resonance(0.55); // "lfo2" waveform overrides this setting | |||
filter1.frequency(800); // "lfo1" modifies this 800 Hz setting | |||
filter1.octaveControl(2.6); // up 2.6 octaves (4850 Hz) & down 2.6 octaves (132 Hz) | |||
waveform1.frequency(50); | |||
waveform2.frequency(100.1); | |||
waveform3.frequency(150.3); | |||
@@ -44,17 +50,22 @@ void setup() { | |||
waveform2.begin(WAVEFORM_BANDLIMIT_SAWTOOTH); | |||
waveform3.begin(WAVEFORM_BANDLIMIT_SAWTOOTH); | |||
lfo1.frequency(0.2); | |||
lfo1.amplitude(0.985); | |||
lfo1.amplitude(0.99); | |||
lfo1.phase(270); | |||
lfo1.begin(WAVEFORM_TRIANGLE); | |||
lfo2.frequency(0.07); | |||
lfo2.amplitude(0.55); | |||
lfo2.phase(270); | |||
lfo2.begin(WAVEFORM_SINE); | |||
} | |||
void loop() { | |||
Serial.print("CPU Usage: "); | |||
Serial.print("Filter CPU Usage: "); | |||
Serial.print(filter1.processorUsageMax()); | |||
Serial.print("%, Total CPU Usage: "); | |||
Serial.print(AudioProcessorUsageMax()); | |||
Serial.println("%"); | |||
filter1.processorUsageMaxReset(); | |||
AudioProcessorUsageMaxReset(); | |||
delay(1000); | |||
} |
@@ -38,6 +38,7 @@ | |||
#define MOOG_PI ((float)3.14159265358979323846264338327950288) | |||
#define MAX_RESONANCE ((float)1.1) | |||
#define MAX_FREQUENCY ((float)(AUDIO_SAMPLE_RATE_EXACT * 0.49f)) | |||
float AudioFilterLadder::LPF(float s, int i) | |||
{ | |||
@@ -65,10 +66,20 @@ void AudioFilterLadder::frequency(float c) | |||
compute_coeffs(c); | |||
} | |||
void AudioFilterLadder::octaveControl(float octaves) | |||
{ | |||
if (octaves > 7.0f) { | |||
octaves = 7.0f; | |||
} else if (octaves < 0.0f) { | |||
octaves = 0.0f; | |||
} | |||
octaveScale = octaves / 32768.0f; | |||
} | |||
void AudioFilterLadder::compute_coeffs(float c) | |||
{ | |||
if (c > 0.49f * AUDIO_SAMPLE_RATE_EXACT) { | |||
c = 0.49f * AUDIO_SAMPLE_RATE_EXACT; | |||
if (c > MAX_FREQUENCY) { | |||
c = MAX_FREQUENCY; | |||
} else if (c < 1.0f) { | |||
c = 1.0f; | |||
} | |||
@@ -86,6 +97,24 @@ bool AudioFilterLadder::resonating() | |||
return false; | |||
} | |||
static inline float fast_exp2f(float x) | |||
{ | |||
float i; | |||
float f = modff(x, &i); | |||
f *= 0.693147f / 256.0f; | |||
f += 1.0f; | |||
f *= f; | |||
f *= f; | |||
f *= f; | |||
f *= f; | |||
f *= f; | |||
f *= f; | |||
f *= f; | |||
f *= f; | |||
f = ldexpf(f, i); | |||
return f; | |||
} | |||
static inline float fast_tanh(float x) | |||
{ | |||
float x2 = x * x; | |||
@@ -128,9 +157,9 @@ void AudioFilterLadder::update(void) | |||
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) { | |||
float input = blocka->data[i] * (1.0f/32768.0f); | |||
if (FCmodActive) { | |||
float FCmod = blockb->data[i] * (1.0f/32768.0f); | |||
// TODO: should this be "volts per octave"? | |||
float ftot = Fbase + Fbase * FCmod; | |||
float FCmod = blockb->data[i] * octaveScale; | |||
float ftot = Fbase * fast_exp2f(FCmod); | |||
if (ftot > MAX_FREQUENCY) ftot = MAX_FREQUENCY; | |||
if (FCmod != 0) compute_coeffs(ftot); | |||
} | |||
if (QmodActive) { |
@@ -42,6 +42,7 @@ public: | |||
AudioFilterLadder() : AudioStream(3, inputQueueArray) {}; | |||
void frequency(float FC); | |||
void resonance(float reson); | |||
void octaveControl(float octaves); | |||
virtual void update(void); | |||
private: | |||
float LPF(float s, int i); | |||
@@ -53,6 +54,7 @@ private: | |||
float z1[4] = {0.0, 0.0, 0.0, 0.0}; | |||
float K = 1.0; | |||
float Fbase = 1000; | |||
float octaveScale = 1.0f/32768.0f; | |||
//float Qbase = 0.5; | |||
//float overdrive = 1.0; | |||
audio_block_t *inputQueueArray[3]; |