Skip to content

Commit de9b0b6

Browse files
authored
Merge pull request #9772 from relic-se/audiofilters_filterlist
2 parents 6d32c56 + c96d142 commit de9b0b6

File tree

4 files changed

+91
-34
lines changed

4 files changed

+91
-34
lines changed

locale/circuitpython.pot

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ msgstr ""
187187
msgid "%q must be >= %d"
188188
msgstr ""
189189

190+
#: shared-module/audiofilters/Filter.c
191+
msgid "%q must be a %q object, %q, or %q"
192+
msgstr ""
193+
190194
#: shared-bindings/analogbufio/BufferedIn.c
191195
msgid "%q must be a bytearray or array of type 'H' or 'B'"
192196
msgstr ""
@@ -1274,6 +1278,7 @@ msgid "Invalid socket for TLS"
12741278
msgstr ""
12751279

12761280
#: ports/espressif/common-hal/espidf/__init__.c
1281+
#: ports/nordic/common-hal/_bleio/__init__.c
12771282
msgid "Invalid state"
12781283
msgstr ""
12791284

@@ -1970,7 +1975,8 @@ msgstr ""
19701975
msgid "The length of rgb_pins must be 6, 12, 18, 24, or 30"
19711976
msgstr ""
19721977

1973-
#: shared-module/audiodelays/Echo.c shared-module/audiomixer/MixerVoice.c
1978+
#: shared-module/audiodelays/Echo.c shared-module/audiofilters/Filter.c
1979+
#: shared-module/audiomixer/MixerVoice.c
19741980
msgid "The sample's %q does not match"
19751981
msgstr ""
19761982

@@ -2504,7 +2510,8 @@ msgstr ""
25042510
msgid "bits must be 32 or less"
25052511
msgstr ""
25062512

2507-
#: shared-bindings/audiodelays/Echo.c shared-bindings/audiomixer/Mixer.c
2513+
#: shared-bindings/audiodelays/Echo.c shared-bindings/audiofilters/Filter.c
2514+
#: shared-bindings/audiomixer/Mixer.c
25082515
msgid "bits_per_sample must be 8 or 16"
25092516
msgstr ""
25102517

shared-bindings/audiofilters/Filter.c

Lines changed: 13 additions & 9 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+
//| filter: Optional[synthio.Biquad | Tuple[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[synthio.Biquad|Tuple[synthio.Biquad]] filter: A normalized biquad filter object or tuple of normalized biquad filter objects. The sample is processed sequentially by each filter to produce the output samples.
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.filter = 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:
@@ -70,7 +71,7 @@
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) {
7172
enum { ARG_filter, 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_filter, 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} },
@@ -128,10 +129,13 @@ 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."""
132+
//| filter: synthio.Biquad | Tuple[synthio.Biquad] | None
133+
//| """A normalized biquad filter object or tuple of normalized biquad filter objects. The sample is processed sequentially by each filter to produce the output samples."""
134+
//|
133135
static mp_obj_t audiofilters_filter_obj_get_filter(mp_obj_t self_in) {
134-
return common_hal_audiofilters_filter_get_filter(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_filter(self);
135139
}
136140
MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_filter_get_filter_obj, audiofilters_filter_obj_get_filter);
137141

shared-module/audiofilters/Filter.c

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,15 @@ void common_hal_audiofilters_filter_construct(audiofilters_filter_obj_t *self,
2828
self->buffer_len = buffer_size; // in bytes
2929

3030
self->buffer[0] = m_malloc(self->buffer_len);
31-
if (self->buffer[0] == NULL) {
32-
common_hal_audiofilters_filter_deinit(self);
33-
m_malloc_fail(self->buffer_len);
34-
}
3531
memset(self->buffer[0], 0, self->buffer_len);
3632

3733
self->buffer[1] = m_malloc(self->buffer_len);
38-
if (self->buffer[1] == NULL) {
39-
common_hal_audiofilters_filter_deinit(self);
40-
m_malloc_fail(self->buffer_len);
41-
}
4234
memset(self->buffer[1], 0, self->buffer_len);
4335

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

4638
// This buffer will be used to process samples through the biquad filter
4739
self->filter_buffer = m_malloc(SYNTHIO_MAX_DUR * sizeof(int32_t));
48-
if (self->filter_buffer == NULL) {
49-
common_hal_audiofilters_filter_deinit(self);
50-
m_malloc_fail(SYNTHIO_MAX_DUR * sizeof(int32_t));
51-
}
5240
memset(self->filter_buffer, 0, SYNTHIO_MAX_DUR * sizeof(int32_t));
5341

5442
// Initialize other values most effects will need.
@@ -63,8 +51,7 @@ void common_hal_audiofilters_filter_construct(audiofilters_filter_obj_t *self,
6351
if (filter == MP_OBJ_NULL) {
6452
filter = mp_const_none;
6553
}
66-
synthio_biquad_filter_assign(&self->filter_state, filter);
67-
self->filter_obj = filter;
54+
common_hal_audiofilters_filter_set_filter(self, filter);
6855

6956
// If we did not receive a BlockInput we need to create a default float value
7057
if (mix == MP_OBJ_NULL) {
@@ -87,15 +74,64 @@ void common_hal_audiofilters_filter_deinit(audiofilters_filter_obj_t *self) {
8774
self->buffer[0] = NULL;
8875
self->buffer[1] = NULL;
8976
self->filter_buffer = NULL;
77+
self->filter_states = NULL;
78+
}
79+
80+
void reset_filter_states(audiofilters_filter_obj_t *self) {
81+
self->filter_states_len = 0;
82+
self->filter_states = NULL;
83+
84+
mp_obj_t *items;
85+
if (mp_obj_is_type(self->filter, (const mp_obj_type_t *)&synthio_biquad_type_obj)) {
86+
self->filter_states_len = 1;
87+
items = self->filter;
88+
} else if (mp_obj_is_tuple_compatible(self->filter)) {
89+
mp_obj_tuple_get(self->filter, &self->filter_states_len, &items);
90+
}
91+
92+
if (!self->filter_states_len) {
93+
return;
94+
}
95+
96+
self->filter_states = m_malloc(self->filter_states_len * sizeof(biquad_filter_state));
97+
98+
if (mp_obj_is_type(items, (const mp_obj_type_t *)&synthio_biquad_type_obj)) {
99+
synthio_biquad_filter_assign(&self->filter_states[0], items);
100+
} else {
101+
for (size_t i = 0; i < self->filter_states_len; i++) {
102+
synthio_biquad_filter_assign(&self->filter_states[i], items[i]);
103+
}
104+
}
90105
}
91106

92107
mp_obj_t common_hal_audiofilters_filter_get_filter(audiofilters_filter_obj_t *self) {
93-
return self->filter_obj;
108+
if (mp_obj_is_type(self->filter, (const mp_obj_type_t *)&synthio_biquad_type_obj) || mp_obj_is_tuple_compatible(self->filter)) {
109+
return self->filter;
110+
} else {
111+
return mp_const_none;
112+
}
94113
}
95114

96115
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;
116+
if (arg == mp_const_none || mp_obj_is_type(arg, (const mp_obj_type_t *)&synthio_biquad_type_obj)) {
117+
self->filter = arg;
118+
} else if (mp_obj_is_tuple_compatible(arg) || mp_obj_is_type(arg, &mp_type_list)) {
119+
size_t tuple_len;
120+
mp_obj_t *tuple_items = NULL;
121+
122+
mp_obj_get_array(arg, &tuple_len, &tuple_items);
123+
124+
mp_obj_t *biquad_objects[tuple_len];
125+
for (size_t i = 0; i < tuple_len; i++) {
126+
biquad_objects[i] = mp_arg_validate_type_in(tuple_items[i], (const mp_obj_type_t *)&synthio_biquad_type_obj, MP_QSTR_filter);
127+
}
128+
129+
self->filter = mp_obj_new_tuple(tuple_len, (const mp_obj_t *)biquad_objects);
130+
} else {
131+
mp_raise_ValueError_varg(MP_ERROR_TEXT("%q must be a %q object, %q, or %q"), MP_QSTR_filter, MP_QSTR_Biquad, MP_QSTR_tuple, MP_QSTR_None);
132+
}
133+
134+
reset_filter_states(self);
99135
}
100136

101137
mp_obj_t common_hal_audiofilters_filter_get_mix(audiofilters_filter_obj_t *self) {
@@ -126,7 +162,11 @@ void audiofilters_filter_reset_buffer(audiofilters_filter_obj_t *self,
126162
memset(self->buffer[1], 0, self->buffer_len);
127163
memset(self->filter_buffer, 0, SYNTHIO_MAX_DUR * sizeof(int32_t));
128164

129-
synthio_biquad_filter_reset(&self->filter_state);
165+
if (self->filter_states) {
166+
for (uint8_t i = 0; i < self->filter_states_len; i++) {
167+
synthio_biquad_filter_reset(&self->filter_states[i]);
168+
}
169+
}
130170
}
131171

132172
bool common_hal_audiofilters_filter_get_playing(audiofilters_filter_obj_t *self) {
@@ -265,7 +305,7 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
265305
int16_t *sample_src = (int16_t *)self->sample_remaining_buffer; // for 16-bit samples
266306
int8_t *sample_hsrc = (int8_t *)self->sample_remaining_buffer; // for 8-bit samples
267307

268-
if (mix <= 0.01 || self->filter_obj == mp_const_none) { // if mix is zero pure sample only or no biquad filter object is provided
308+
if (mix <= 0.01 || !self->filter_states) { // if mix is zero pure sample only or no biquad filter objects are provided
269309
for (uint32_t i = 0; i < n; i++) {
270310
if (MP_LIKELY(self->bits_per_sample == 16)) {
271311
word_buffer[i] = sample_src[i];
@@ -292,8 +332,10 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
292332
}
293333
}
294334

295-
// Process biquad filter
296-
synthio_biquad_filter_samples(&self->filter_state, self->filter_buffer, n_samples);
335+
// Process biquad filters
336+
for (uint8_t j = 0; j < self->filter_states_len; j++) {
337+
synthio_biquad_filter_samples(&self->filter_states[j], self->filter_buffer, n_samples);
338+
}
297339

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

shared-module/audiofilters/Filter.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "py/obj.h"
99

10+
#include "shared-bindings/synthio/Biquad.h"
1011
#include "shared-module/audiocore/__init__.h"
1112
#include "shared-module/synthio/block.h"
1213
#include "shared-module/synthio/Biquad.h"
@@ -15,10 +16,11 @@ extern const mp_obj_type_t audiofilters_filter_type;
1516

1617
typedef struct {
1718
mp_obj_base_t base;
18-
mp_obj_t filter_obj;
19+
mp_obj_t *filter;
1920
synthio_block_slot_t mix;
2021

21-
biquad_filter_state filter_state;
22+
size_t filter_states_len;
23+
biquad_filter_state *filter_states;
2224

2325
uint8_t bits_per_sample;
2426
bool samples_signed;
@@ -40,6 +42,8 @@ typedef struct {
4042
mp_obj_t sample;
4143
} audiofilters_filter_obj_t;
4244

45+
void reset_filter_states(audiofilters_filter_obj_t *self);
46+
4347
void audiofilters_filter_reset_buffer(audiofilters_filter_obj_t *self,
4448
bool single_channel_output,
4549
uint8_t channel);

0 commit comments

Comments
 (0)