Skip to content

Commit 6badb92

Browse files
committed
Add support for list of Biquad objects within audiofilters.Filter.
1 parent 8bb9f86 commit 6badb92

File tree

4 files changed

+80
-42
lines changed

4 files changed

+80
-42
lines changed

shared-bindings/audiofilters/Filter.c

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
//|
2424
//| def __init__(
2525
//| self,
26-
//| filter: Optional[synthio.Biquad] = None,
26+
//| filters: Optional[List[synthio.Biquad]] = None,
2727
//| mix: synthio.BlockInput = 1.0,
2828
//| buffer_size: int = 512,
2929
//| sample_rate: int = 8000,
@@ -38,7 +38,7 @@
3838
//| The mix parameter allows you to change how much of the unchanged sample passes through to
3939
//| the output to how much of the effect audio you hear as the output.
4040
//|
41-
//| :param Optional[synthio.Biquad] filter: The normalized biquad filter object used to process the signal.
41+
//| :param Optional[List[synthio.Biquad]] filters: A list of normalized biquad filter objects used to process the signal.
4242
//| :param synthio.BlockInput mix: The mix as a ratio of the sample (0.0) to the effect (1.0).
4343
//| :param int buffer_size: The total size in bytes of each of the two playback buffers to use
4444
//| :param int sample_rate: The sample rate to be used
@@ -56,9 +56,10 @@
5656
//|
5757
//| audio = audiobusio.I2SOut(bit_clock=board.GP20, word_select=board.GP21, data=board.GP22)
5858
//| synth = synthio.Synthesizer(channel_count=1, sample_rate=44100)
59-
//| filter = audiofilters.Filter(filter=synth.low_pass_filter(frequency=2000, Q=1.25), buffer_size=1024, channel_count=1, sample_rate=44100, mix=1.0)
60-
//| filter.play(synth)
61-
//| audio.play(filter)
59+
//| effect = audiofilters.Filter(buffer_size=1024, channel_count=1, sample_rate=44100, mix=1.0)
60+
//| effect.filters.append(synth.low_pass_filter(frequency=2000, Q=1.25))
61+
//| effect.play(synth)
62+
//| audio.play(effect)
6263
//|
6364
//| note = synthio.Note(261)
6465
//| while True:
@@ -68,9 +69,9 @@
6869
//| time.sleep(5)"""
6970
//| ...
7071
static mp_obj_t audiofilters_filter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
71-
enum { ARG_filter, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, };
72+
enum { ARG_filters, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, };
7273
static const mp_arg_t allowed_args[] = {
73-
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} },
74+
{ MP_QSTR_filters, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} },
7475
{ MP_QSTR_mix, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} },
7576
{ MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 512} },
7677
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} },
@@ -90,7 +91,7 @@ static mp_obj_t audiofilters_filter_make_new(const mp_obj_type_t *type, size_t n
9091
}
9192

9293
audiofilters_filter_obj_t *self = mp_obj_malloc(audiofilters_filter_obj_t, &audiofilters_filter_type);
93-
common_hal_audiofilters_filter_construct(self, args[ARG_filter].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate);
94+
common_hal_audiofilters_filter_construct(self, args[ARG_filters].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate);
9495

9596
return MP_OBJ_FROM_PTR(self);
9697
}
@@ -128,31 +129,34 @@ static mp_obj_t audiofilters_filter_obj___exit__(size_t n_args, const mp_obj_t *
128129
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiofilters_filter___exit___obj, 4, 4, audiofilters_filter_obj___exit__);
129130

130131

131-
//| filter: Optional[synthio.Biquad]
132-
//| """The normalized biquad filter object used to process the signal."""
133-
static mp_obj_t audiofilters_filter_obj_get_filter(mp_obj_t self_in) {
134-
return common_hal_audiofilters_filter_get_filter(self_in);
132+
//| filters: List[synthio.Biquad]
133+
//| """A list of normalized biquad filter objects used to process the signal."""
134+
//|
135+
static mp_obj_t audiofilters_filter_obj_get_filters(mp_obj_t self_in) {
136+
audiofilters_filter_obj_t *self = MP_OBJ_TO_PTR(self_in);
137+
check_for_deinit(self);
138+
return common_hal_audiofilters_filter_get_filters(self);
135139
}
136-
MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_filter_get_filter_obj, audiofilters_filter_obj_get_filter);
140+
MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_filter_get_filters_obj, audiofilters_filter_obj_get_filters);
137141

138-
static mp_obj_t audiofilters_filter_obj_set_filter(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
139-
enum { ARG_filter };
142+
static mp_obj_t audiofilters_filter_obj_set_filters(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
143+
enum { ARG_filters };
140144
static const mp_arg_t allowed_args[] = {
141-
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_REQUIRED, {} },
145+
{ MP_QSTR_filters, MP_ARG_OBJ | MP_ARG_REQUIRED, {} },
142146
};
143147
audiofilters_filter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
144148
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
145149
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
146150

147-
common_hal_audiofilters_filter_set_filter(self, args[ARG_filter].u_obj);
151+
common_hal_audiofilters_filter_set_filters(self, args[ARG_filters].u_obj);
148152

149153
return mp_const_none;
150154
}
151-
MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_set_filter_obj, 1, audiofilters_filter_obj_set_filter);
155+
MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_set_filters_obj, 1, audiofilters_filter_obj_set_filters);
152156

153-
MP_PROPERTY_GETSET(audiofilters_filter_filter_obj,
154-
(mp_obj_t)&audiofilters_filter_get_filter_obj,
155-
(mp_obj_t)&audiofilters_filter_set_filter_obj);
157+
MP_PROPERTY_GETSET(audiofilters_filter_filters_obj,
158+
(mp_obj_t)&audiofilters_filter_get_filters_obj,
159+
(mp_obj_t)&audiofilters_filter_set_filters_obj);
156160

157161

158162
//| mix: synthio.BlockInput
@@ -241,7 +245,7 @@ static const mp_rom_map_elem_t audiofilters_filter_locals_dict_table[] = {
241245

242246
// Properties
243247
{ MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiofilters_filter_playing_obj) },
244-
{ MP_ROM_QSTR(MP_QSTR_filter), MP_ROM_PTR(&audiofilters_filter_filter_obj) },
248+
{ MP_ROM_QSTR(MP_QSTR_filters), MP_ROM_PTR(&audiofilters_filter_filters_obj) },
245249
{ MP_ROM_QSTR(MP_QSTR_mix), MP_ROM_PTR(&audiofilters_filter_mix_obj) },
246250
};
247251
static MP_DEFINE_CONST_DICT(audiofilters_filter_locals_dict, audiofilters_filter_locals_dict_table);

shared-bindings/audiofilters/Filter.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
extern const mp_obj_type_t audiofilters_filter_type;
1212

1313
void common_hal_audiofilters_filter_construct(audiofilters_filter_obj_t *self,
14-
mp_obj_t filter, mp_obj_t mix,
14+
mp_obj_t filters, mp_obj_t mix,
1515
uint32_t buffer_size, uint8_t bits_per_sample, bool samples_signed,
1616
uint8_t channel_count, uint32_t sample_rate);
1717

@@ -22,8 +22,8 @@ uint32_t common_hal_audiofilters_filter_get_sample_rate(audiofilters_filter_obj_
2222
uint8_t common_hal_audiofilters_filter_get_channel_count(audiofilters_filter_obj_t *self);
2323
uint8_t common_hal_audiofilters_filter_get_bits_per_sample(audiofilters_filter_obj_t *self);
2424

25-
mp_obj_t common_hal_audiofilters_filter_get_filter(audiofilters_filter_obj_t *self);
26-
void common_hal_audiofilters_filter_set_filter(audiofilters_filter_obj_t *self, mp_obj_t arg);
25+
mp_obj_t common_hal_audiofilters_filter_get_filters(audiofilters_filter_obj_t *self);
26+
void common_hal_audiofilters_filter_set_filters(audiofilters_filter_obj_t *self, mp_obj_t arg);
2727

2828
mp_obj_t common_hal_audiofilters_filter_get_mix(audiofilters_filter_obj_t *self);
2929
void common_hal_audiofilters_filter_set_mix(audiofilters_filter_obj_t *self, mp_obj_t arg);

shared-module/audiofilters/Filter.c

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include "py/runtime.h"
1010

1111
void common_hal_audiofilters_filter_construct(audiofilters_filter_obj_t *self,
12-
mp_obj_t filter, mp_obj_t mix,
12+
mp_obj_t filters, mp_obj_t mix,
1313
uint32_t buffer_size, uint8_t bits_per_sample,
1414
bool samples_signed, uint8_t channel_count, uint32_t sample_rate) {
1515

@@ -60,12 +60,12 @@ void common_hal_audiofilters_filter_construct(audiofilters_filter_obj_t *self,
6060

6161
// The below section sets up the effect's starting values.
6262

63-
if (filter == MP_OBJ_NULL) {
64-
filter = mp_const_none;
63+
if (filters == MP_OBJ_NULL) {
64+
filters = mp_obj_new_list(0, NULL);
6565
}
66-
synthio_biquad_filter_assign(&self->filter_state, filter);
67-
self->filter_obj = filter;
68-
66+
self->filters = filters;
67+
reset_filter_states(self);
68+
6969
// If we did not receive a BlockInput we need to create a default float value
7070
if (mix == MP_OBJ_NULL) {
7171
mix = mp_obj_new_float(1.0);
@@ -87,15 +87,37 @@ void common_hal_audiofilters_filter_deinit(audiofilters_filter_obj_t *self) {
8787
self->buffer[0] = NULL;
8888
self->buffer[1] = NULL;
8989
self->filter_buffer = NULL;
90+
self->filter_states = NULL;
91+
}
92+
93+
void reset_filter_states(audiofilters_filter_obj_t *self) {
94+
self->filter_states_len = self->filters->len;
95+
self->filter_states = NULL;
96+
97+
if (self->filter_states_len) {
98+
self->filter_states = m_malloc(self->filter_states_len * sizeof(biquad_filter_state));
99+
if (self->filter_states == NULL) {
100+
common_hal_audiofilters_filter_deinit(self);
101+
m_malloc_fail(self->filter_states_len * sizeof(biquad_filter_state));
102+
}
103+
104+
mp_obj_iter_buf_t iter_buf;
105+
mp_obj_t iterable = mp_getiter(self->filters, &iter_buf);
106+
mp_obj_t item;
107+
uint8_t i = 0;
108+
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
109+
synthio_biquad_filter_assign(&self->filter_states[i++], item);
110+
}
111+
}
90112
}
91113

92-
mp_obj_t common_hal_audiofilters_filter_get_filter(audiofilters_filter_obj_t *self) {
93-
return self->filter_obj;
114+
mp_obj_t common_hal_audiofilters_filter_get_filters(audiofilters_filter_obj_t *self) {
115+
return self->filters;
94116
}
95117

96-
void common_hal_audiofilters_filter_set_filter(audiofilters_filter_obj_t *self, mp_obj_t arg) {
97-
synthio_biquad_filter_assign(&self->filter_state, arg);
98-
self->filter_obj = arg;
118+
void common_hal_audiofilters_filter_set_filters(audiofilters_filter_obj_t *self, mp_obj_t arg) {
119+
self->filters = arg;
120+
reset_filter_states(self);
99121
}
100122

101123
mp_obj_t common_hal_audiofilters_filter_get_mix(audiofilters_filter_obj_t *self) {
@@ -126,7 +148,9 @@ void audiofilters_filter_reset_buffer(audiofilters_filter_obj_t *self,
126148
memset(self->buffer[1], 0, self->buffer_len);
127149
memset(self->filter_buffer, 0, SYNTHIO_MAX_DUR * sizeof(int32_t));
128150

129-
synthio_biquad_filter_reset(&self->filter_state);
151+
for (uint8_t i = 0; i < self->filter_states_len; i++) {
152+
synthio_biquad_filter_reset(&self->filter_states[i]);
153+
}
130154
}
131155

132156
bool common_hal_audiofilters_filter_get_playing(audiofilters_filter_obj_t *self) {
@@ -209,6 +233,11 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
209233
channel = 0;
210234
}
211235

236+
// Update filter_states if filters list has been appended or removed
237+
if (self->filters->len != self->filter_states_len) {
238+
reset_filter_states(self);
239+
}
240+
212241
// get the effect values we need from the BlockInput. These may change at run time so you need to do bounds checking if required
213242
mp_float_t mix = MIN(1.0, MAX(synthio_block_slot_get(&self->mix), 0.0));
214243

@@ -248,7 +277,7 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
248277
int16_t *sample_src = (int16_t *)self->sample_remaining_buffer; // for 16-bit samples
249278
int8_t *sample_hsrc = (int8_t *)self->sample_remaining_buffer; // for 8-bit samples
250279

251-
if (mix <= 0.01 || self->filter_obj == mp_const_none) { // if mix is zero pure sample only or no biquad filter object is provided
280+
if (mix <= 0.01 || !self->filter_states_len) { // if mix is zero pure sample only or no biquad filter objects are provided
252281
for (uint32_t i = 0; i < n; i++) {
253282
if (MP_LIKELY(self->bits_per_sample == 16)) {
254283
word_buffer[i] = sample_src[i];
@@ -275,8 +304,10 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
275304
}
276305
}
277306

278-
// Process biquad filter
279-
synthio_biquad_filter_samples(&self->filter_state, self->filter_buffer, n_samples);
307+
// Process biquad filters
308+
for (uint8_t j = 0; j < self->filter_states_len; j++) {
309+
synthio_biquad_filter_samples(&self->filter_states[j], self->filter_buffer, n_samples);
310+
}
280311

281312
// Mix processed signal with original sample and transfer to output buffer
282313
for (uint32_t j = 0; j < n_samples; j++) {

shared-module/audiofilters/Filter.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ extern const mp_obj_type_t audiofilters_filter_type;
1515

1616
typedef struct {
1717
mp_obj_base_t base;
18-
mp_obj_t filter_obj;
18+
mp_obj_list_t *filters;
1919
synthio_block_slot_t mix;
2020

21-
biquad_filter_state filter_state;
21+
uint8_t filter_states_len;
22+
biquad_filter_state *filter_states;
2223

2324
uint8_t bits_per_sample;
2425
bool samples_signed;
@@ -40,6 +41,8 @@ typedef struct {
4041
mp_obj_t sample;
4142
} audiofilters_filter_obj_t;
4243

44+
void reset_filter_states(audiofilters_filter_obj_t *self);
45+
4346
void audiofilters_filter_reset_buffer(audiofilters_filter_obj_t *self,
4447
bool single_channel_output,
4548
uint8_t channel);

0 commit comments

Comments
 (0)