-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
160 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Houtson 2020 | ||
// | ||
// Implementation of the Moog ladder filter based on the work of Antti | ||
// Huovilainen, described in the paper "Non-Linear Digital Implementation of the | ||
// Moog Ladder Filter" (Proceedings of DaFX04, University of Napoli). Adapted | ||
// from a Reaper JS source file at | ||
// http://ajaxsoundstudio.com/cookdspdoc/moog.html | ||
// | ||
// | ||
|
||
#include "effect_anttimoog.h" | ||
#include <Arduino.h> | ||
|
||
#ifndef TEENSY_ANTTI_FILTERCPP_H | ||
#define TEENSY_ANTTI_FILTERCPP_H | ||
#define DIAG_PER_SAMPLES 0 // set to 0 for no DIAG or >0 for "print some diag every x samples" e.g. 500 | ||
|
||
AudioEffectAnttiMoog::AudioEffectAnttiMoog(float sampleRate) { | ||
filter_y1 = filter_y2 = filter_y3 = filter_y4 = filter_y5 = filter_out = 0; | ||
srate = sampleRate; | ||
filter_nylimit = srate * 0.49; | ||
filter_z = 1.0 / 40000.0; // twice the "thermal voltage of a transistor" | ||
SetCutoff(1000.0f); | ||
SetResonance(0.1f); | ||
compute_coeffs(filter_freq, filter_res); | ||
}; | ||
|
||
void compute_coeffs(float freq, float res) { | ||
float f_, fc_, fc2_, fc3_, fcr_, acr_; | ||
if (freq < 1) { | ||
freq = 1; | ||
} else { | ||
if (freq > filter_nylimit) freq = filter_nylimit; | ||
} | ||
if (res < 0) { | ||
res = 0; | ||
} else { | ||
if (res > 1) res = 1; | ||
} | ||
filter_freq = freq; | ||
filter_res = res; | ||
// srate is half the actual filter sampling rate | ||
fc_ = filter_freq / srate; | ||
f_ = 0.5 * fc_; | ||
fc2_ = fc_ * fc_; | ||
fc3_ = fc2_ * fc_; | ||
// frequency & amplitude correction | ||
fcr_ = 1.8730 * fc3_ + 0.4955 * fc2_ - 0.6490 * fc_ + 0.9988; | ||
acr_ = -3.9364 * fc2_ + 1.8409 * fc_ + 0.9968; | ||
filter_scl = float((1.0 - exp(-2 * AM_PI * f_ * fcr_)) / filter_z); // filter tuning | ||
filter_r4 = 3.96 * filter_res * acr_; // filter feedback | ||
}; | ||
|
||
void SetCutoff(float freq) { | ||
if (freq != filter_freq) compute_coeffs(freq, filter_res); | ||
}; | ||
|
||
void SetResonance(float res) { | ||
if (res != filter_res) compute_coeffs(filter_freq, res); | ||
}; | ||
|
||
virtual void Process(float *samples, uint32_t n) { | ||
for (uint32_t s = 0; s < n; ++s) { | ||
for (int a = 0; a < 2; a++) { | ||
filter_y1 = filter_y1 + filter_scl * (tanh((samples[s] - filter_r4 * filter_out) * filter_z) - tanh(filter_y1 * filter_z)); | ||
filter_y2 = filter_y2 + filter_scl * (tanh(filter_y1 * filter_z) - tanh(filter_y2 * filter_z)); | ||
filter_y3 = filter_y3 + filter_scl * (tanh(filter_y2 * filter_z) - tanh(filter_y3 * filter_z)); | ||
filter_y4 = filter_y4 + filter_scl * (tanh(filter_y3 * filter_z) - tanh(filter_y4 * filter_z)); | ||
filter_out = (filter_y4 + filter_y5) * 0.5; | ||
filter_y5 = filter_y4; | ||
} | ||
samples[s] = filter_out; | ||
} | ||
}; | ||
|
||
void AudioEffectAnttiMoog::update(void) { | ||
uint16_t sample_data_unsigned; | ||
audio_block_t *block; | ||
|
||
block = receiveWritable(0); | ||
if (!block) return; | ||
|
||
for (uint32_t s = 0; s < AUDIO_BLOCK_SAMPLES; ++s) { | ||
float current_sample = block->data[s] * INV_WAVE_AMP; | ||
for (int a = 0; a < 2; a++) { | ||
filter_y1 = filter_y1 + filter_scl * (tanh((current_sample - filter_r4 * filter_out) * filter_z) - tanh(filter_y1 * filter_z)); | ||
filter_y2 = filter_y2 + filter_scl * (tanh(filter_y1 * filter_z) - tanh(filter_y2 * filter_z)); | ||
filter_y3 = filter_y3 + filter_scl * (tanh(filter_y2 * filter_z) - tanh(filter_y3 * filter_z)); | ||
filter_y4 = filter_y4 + filter_scl * (tanh(filter_y3 * filter_z) - tanh(filter_y4 * filter_z)); | ||
filter_out = (filter_y4 + filter_y5) * 0.5; | ||
filter_y5 = filter_y4; | ||
} | ||
block->data[s] = filter_out * WAVE_AMP; | ||
} | ||
|
||
// print some diags | ||
if (DIAG_PER_SAMPLES && diag_count > DIAG_PER_SAMPLES) { | ||
diag_count = 0; | ||
} | ||
if (DIAG_PER_SAMPLES) diag_count++; | ||
|
||
transmit(block); | ||
release(block); | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Houtson 2020 | ||
// | ||
// Implementation of the Moog ladder filter based on the work of Antti | ||
// Huovilainen, described in the paper "Non-Linear Digital Implementation of the | ||
// Moog Ladder Filter" (Proceedings of DaFX04, University of Napoli). Adapted | ||
// from a Reaper JS source file at | ||
// http://ajaxsoundstudio.com/cookdspdoc/moog.html | ||
// | ||
// | ||
|
||
#include <math.h> | ||
#include <stdint.h> | ||
#include "AudioStream.h" | ||
|
||
#ifndef TEENSY_ANTTI_FILTER_H | ||
#define TEENSY_ANTTI_FILTER_H | ||
|
||
#define AM_PI 3.14159265358979323846 /* pi */ | ||
#define WAVE_MAX 32768.0 | ||
//===================================================== | ||
// amplitude of signal. Should be less than WAVE_MAX | ||
#define WAVE_AMP 32000.0f | ||
// From martianredskies | ||
#define INV_WAVE_AMP (1.f / 32000.0f) | ||
|
||
|
||
class AudioEffectAnttiMoog : public AudioStream { | ||
public: | ||
AudioEffectAnttiMoog(float sampleRate) : AudioStream(1, inputQueueArray) {} | ||
void compute_coeffs(float freq, float res); | ||
void SetCutoff(float freq); | ||
void SetResonance(float res); | ||
virtual void Process(float *samples, uint32_t n); | ||
virtual void update(void); | ||
|
||
private: | ||
|
||
audio_block_t *inputQueueArray[1]; | ||
int16_t *sample_bank; | ||
int16_t nibble_effect_mode; | ||
int16_t prev_input; | ||
uint16_t bitmask_mute; | ||
uint16_t bitmask_invert; | ||
uint16_t bitmask_squeeze; | ||
bool waveform_negative; | ||
int diag_count = 0; | ||
|
||
float srate; | ||
float filter_scl, filter_z, filter_r4, filter_res, filter_y1, filter_y2, filter_y3, filter_y4, filter_y5, filter_out, filter_freq, filter_nylimit; | ||
uint32_t count = 0; | ||
bool print_out = false; | ||
}; | ||
#endif | ||
|
||
|