| @@ -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]; | |||