// By Richard van Hoesel | // By Richard van Hoesel | ||||
// https://forum.pjrc.com/threads/60488?p=269756&viewfull=1#post269756 | // 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> | #include <Audio.h> | ||||
AudioSynthWaveform waveform1; | AudioSynthWaveform waveform1; | ||||
sgtl5000_1.enable(); | sgtl5000_1.enable(); | ||||
sgtl5000_1.volume(0.6); | 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); | waveform1.frequency(50); | ||||
waveform2.frequency(100.1); | waveform2.frequency(100.1); | ||||
waveform3.frequency(150.3); | waveform3.frequency(150.3); | ||||
waveform2.begin(WAVEFORM_BANDLIMIT_SAWTOOTH); | waveform2.begin(WAVEFORM_BANDLIMIT_SAWTOOTH); | ||||
waveform3.begin(WAVEFORM_BANDLIMIT_SAWTOOTH); | waveform3.begin(WAVEFORM_BANDLIMIT_SAWTOOTH); | ||||
lfo1.frequency(0.2); | lfo1.frequency(0.2); | ||||
lfo1.amplitude(0.985); | |||||
lfo1.amplitude(0.99); | |||||
lfo1.phase(270); | lfo1.phase(270); | ||||
lfo1.begin(WAVEFORM_TRIANGLE); | |||||
lfo2.frequency(0.07); | lfo2.frequency(0.07); | ||||
lfo2.amplitude(0.55); | lfo2.amplitude(0.55); | ||||
lfo2.phase(270); | lfo2.phase(270); | ||||
lfo2.begin(WAVEFORM_SINE); | |||||
} | } | ||||
void loop() { | void loop() { | ||||
Serial.print("CPU Usage: "); | |||||
Serial.print("Filter CPU Usage: "); | |||||
Serial.print(filter1.processorUsageMax()); | |||||
Serial.print("%, Total CPU Usage: "); | |||||
Serial.print(AudioProcessorUsageMax()); | Serial.print(AudioProcessorUsageMax()); | ||||
Serial.println("%"); | Serial.println("%"); | ||||
filter1.processorUsageMaxReset(); | |||||
AudioProcessorUsageMaxReset(); | AudioProcessorUsageMaxReset(); | ||||
delay(1000); | delay(1000); | ||||
} | } |
#define MOOG_PI ((float)3.14159265358979323846264338327950288) | #define MOOG_PI ((float)3.14159265358979323846264338327950288) | ||||
#define MAX_RESONANCE ((float)1.1) | #define MAX_RESONANCE ((float)1.1) | ||||
#define MAX_FREQUENCY ((float)(AUDIO_SAMPLE_RATE_EXACT * 0.49f)) | |||||
float AudioFilterLadder::LPF(float s, int i) | float AudioFilterLadder::LPF(float s, int i) | ||||
{ | { | ||||
compute_coeffs(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) | 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) { | } else if (c < 1.0f) { | ||||
c = 1.0f; | c = 1.0f; | ||||
} | } | ||||
return false; | 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) | static inline float fast_tanh(float x) | ||||
{ | { | ||||
float x2 = x * x; | float x2 = x * x; | ||||
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) { | for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) { | ||||
float input = blocka->data[i] * (1.0f/32768.0f); | float input = blocka->data[i] * (1.0f/32768.0f); | ||||
if (FCmodActive) { | 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 (FCmod != 0) compute_coeffs(ftot); | ||||
} | } | ||||
if (QmodActive) { | if (QmodActive) { |
AudioFilterLadder() : AudioStream(3, inputQueueArray) {}; | AudioFilterLadder() : AudioStream(3, inputQueueArray) {}; | ||||
void frequency(float FC); | void frequency(float FC); | ||||
void resonance(float reson); | void resonance(float reson); | ||||
void octaveControl(float octaves); | |||||
virtual void update(void); | virtual void update(void); | ||||
private: | private: | ||||
float LPF(float s, int i); | float LPF(float s, int i); | ||||
float z1[4] = {0.0, 0.0, 0.0, 0.0}; | float z1[4] = {0.0, 0.0, 0.0, 0.0}; | ||||
float K = 1.0; | float K = 1.0; | ||||
float Fbase = 1000; | float Fbase = 1000; | ||||
float octaveScale = 1.0f/32768.0f; | |||||
//float Qbase = 0.5; | //float Qbase = 0.5; | ||||
//float overdrive = 1.0; | //float overdrive = 1.0; | ||||
audio_block_t *inputQueueArray[3]; | audio_block_t *inputQueueArray[3]; |