10000 Implement allpass filters. · FoamyGuy/circuitpython@ba81c6b · GitHub
[go: up one dir, main page]

Skip to content

Commit ba81c6b

Browse files
committed
Implement allpass filters.
1 parent e729849 commit ba81c6b

File tree

4 files changed

+43
-13
lines changed

4 files changed

+43
-13
lines changed

shared-bindings/audiofilters/Phaser.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@
5353
//| import time
5454
//| import board
5555
//| import audiobusio
56-
//| import synthio
5756
//| import audiofilters
57+
//| import synthio
5858
//|
5959
//| audio = audiobusio.I2SOut(bit_clock=board.GP20, word_select=board.GP21, data=board.GP22)
6060
//| synth = synthio.Synthesizer(channel_count=1, sample_rate=44100)
61-
//| effect = audiofilters.Phaser(buffer_size=1024, channel_count=1, sample_rate=44100, mix=1.0)
61+
//| effect = audiofilters.Phaser(channel_count=1, sample_rate=44100)
6262
//| effect.frequency = synthio.LFO(offset=1000.0, scale=600.0, rate=0.5)
6363
//| effect.play(synth)
6464
//| audio.play(effect)
@@ -67,7 +67,7 @@
6767
//| ...
6868
//|
6969
static mp_obj_t audiofilters_phaser_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
70-
enum { ARG_frequency, ARG_stages, ARG_feedback, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, };
70+
enum { ARG_frequency, ARG_feedback, ARG_mix, ARG_stages, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, };
7171
static const mp_arg_t allowed_args[] = {
7272
{ MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1000) } },
7373
{ MP_QSTR_feedback, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },

shared-bindings/audiofilters/Phaser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mp_obj_t common_hal_audiofilters_phaser_get_mix(audiofilters_phaser_obj_t *self)
2727
void common_hal_audiofilters_phaser_set_mix(audiofilters_phaser_obj_t *self, mp_obj_t arg);
2828

2929
uint8_t common_hal_audiofilters_phaser_get_stages(audiofilters_phaser_obj_t *self);
30-
void common_hal_audiofilters_phaser_set_stages(audiofilters_phaser_obj_t *self, mp_obj_t arg);
30+
void common_hal_audiofilters_phaser_set_stages(audiofilters_phaser_obj_t *self, uint8_t arg);
3131

3232
bool common_hal_audiofilters_phaser_get_playing(audiofilters_phaser_obj_t *self);
3333
void common_hal_audiofilters_phaser_play(audiofilters_phaser_obj_t *self, mp_obj_t sample, bool loop);

shared-module/audiofilters/Phaser.c

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ void common_hal_audiofilters_phaser_construct(audiofilters_phaser_obj_t *self,
3838

3939
self->last_buf_idx = 1; // Which buffer to use first, toggle between 0 and 1
4040

41-
// This buffer will be used to process samples through the biquad filter
42-
self->filter_buffer = m_malloc_without_collect(SYNTHIO_MAX_DUR * sizeof(int32_t));
43-
memset(self->filter_buffer, 0, SYNTHIO_MAX_DUR * sizeof(int32_t));
44-
4541
// Initialize other values most effects will need.
4642
self->sample = NULL; // The current playing sample
4743
self->sample_remaining_buffer = NULL; // Pointer to the start of the sample buffer we have not played
@@ -51,7 +47,11 @@ void common_hal_audiofilters_phaser_construct(audiofilters_phaser_obj_t *self,
5147

5248
// The below section sets up the effect's starting values.
5349

54-
self->nyquist = (mp_float_t) self->base.sample_rate / 2;
50+
// Create buffer to hold the last processed word
51+
self->word_buffer = m_malloc_without_collect(self->base.channel_count * sizeof(int32_t));
52+
memset(self->word_buffer, 0, self->base.channel_count * sizeof(int32_t));
53+
54+
self->nyquist = (mp_float_t)self->base.sample_rate / 2;
5555

5656
if (feedback == mp_const_none) {
5757
feedback = mp_obj_new_float(MICROPY_FLOAT_CONST(0.7));
@@ -68,6 +68,8 @@ void common_hal_audiofilters_phaser_deinit(audiofilters_phaser_obj_t *self) {
6868
audiosample_mark_deinit(&self->base);
6969
self->buffer[0] = NULL;
7070
self->buffer[1] = NULL;
71+
self->word_buffer = NULL;
72+
self->allpass_buffer = NULL;
7173
}
7274

7375
mp_obj_t common_hal_audiofilters_phaser_get_frequency(audiofilters_phaser_obj_t *self) {
@@ -102,8 +104,15 @@ void common_hal_audiofilters_phaser_set_stages(audiofilters_phaser_obj_t *self,
102104
if (!arg) {
103105
arg = 1;
104106
}
105-
// TODO: reallocate filters
107+
108+
self->allpass_buffer = (int32_t *)m_realloc(self->allpass_buffer,
109+
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
110+
self->base.channel_count * self->stages * sizeof(int32_t), // Old size
111+
#endif
112+
self->base.channel_count * arg * sizeof(int32_t));
106113
self->stages = arg;
114+
115+
memset(self->allpass_buffer, 0, self->base.channel_count * self->stages * sizeof(int32_t));
107116
}
108117

109118
void audiofilters_phaser_reset_buffer(audiofilters_phaser_obj_t *self,
@@ -112,6 +121,8 @@ void audiofilters_phaser_reset_buffer(audiofilters_phaser_obj_t *self,
112121

113122
memset(self->buffer[0], 0, self->buffer_len);
114123
memset(self->buffer[1], 0, self->buffer_len);
124+
memset(self->word_buffer, 0, self->base.channel_count * sizeof(int32_t));
125+
memset(self->allpass_buffer, 0, self->base.channel_count * self->stages * sizeof(int32_t));
115126
}
116127

117128
bool common_hal_audiofilters_phaser_get_playing(audiofilters_phaser_obj_t *self) {
@@ -222,7 +233,15 @@ audioio_get_buffer_result_t audiofilters_phaser_get_buffer(audiofilters_phaser_o
222233
}
223234
}
224235
} else {
236+
237+
// Update all-pass filter coefficient
238+
mp_float_t allpasscoef = frequency / self->nyquist;
239+
allpasscoef = (MICROPY_FLOAT_CONST(1.0) - allpasscoef) / (MICROPY_FLOAT_CONST(1.0) + allpasscoef);
240+
225241
for (uint32_t i = 0; i < n; i++) {
242+
bool right_channel = (single_channel_output && channel == 1) || (!single_channel_output && (i % self->base.channel_count) == 1);
243+
uint32_t allpass_buffer_offset = self->stages * right_channel;
244+
226245
int32_t sample_word = 0;
227246
if (MP_LIKELY(self->base.bits_per_sample == 16)) {
228247
sample_word = sample_src[i];
@@ -235,13 +254,21 @@ audioio_get_buffer_result_t audiofilters_phaser_get_buffer(audiofilters_phaser_o
235254
}
236255
}
237256

238-
// TODO: Process sample
239-
int32_t word = 0;
257+
int32_t word = sample_word + self->word_buffer[right_channel] * feedback;
258+
int32_t allpass_word = 0;
259+
260+
// Update all-pass filters
261+
for (uint32_t j = 0; j < self->stages; j++) {
262+
allpass_word = word * -allpasscoef + self->allpass_buffer[j + allpass_buffer_offset];
263+
self->allpass_buffer[j + allpass_buffer_offset] = allpass_word * allpasscoef + word;
264+
word = allpass_word;
265+
}
266+
self->word_buffer[(bool)allpass_buffer_offset] = word;
240267

241268
// Add original sample + effect
242269
word = sample_word + (int32_t)(word * mix);
243270
word = synthio_mix_down_sample(word, 2);
244-
271+
245272
if (MP_LIKELY(self->base.bits_per_sample == 16)) {
246273
word_buffer[i] = word;
247274
if (!self->base.samples_signed) {

shared-module/audiofilters/Phaser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ typedef struct {
3232
bool loop;
3333
bool more_data;
3434

35+
int32_t *allpass_buffer;
36+
int32_t *word_buffer;
37+
3538
mp_obj_t sample;
3639
} audiofilters_phaser_obj_t;
3740

0 commit comments

Comments
 (0)
0