Skip to content

Commit 941e122

Browse files
committed
Rename filters back to filter and support individual Biquad object and tuple of Biquad objects.
1 parent 14b1383 commit 941e122

File tree

5 files changed

+88
-57
lines changed

5 files changed

+88
-57
lines changed

locale/circuitpython.pot

+9-2
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

+20-20
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
//|
2424
//| def __init__(
2525
//| self,
26-
//| filters: Optional[List[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[List[synthio.Biquad]] filters: A list of normalized biquad filter objects 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
@@ -57,7 +57,7 @@
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)
5959
//| 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))
60+
//| effect.filter = synth.low_pass_filter(frequency=2000, Q=1.25)
6161
//| effect.play(synth)
6262
//| audio.play(effect)
6363
//|
@@ -69,9 +69,9 @@
6969
//| time.sleep(5)"""
7070
//| ...
7171
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) {
72-
enum { ARG_filters, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, };
72+
enum { ARG_filter, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, };
7373
static const mp_arg_t allowed_args[] = {
74-
{ MP_QSTR_filters, 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} },
7575
{ MP_QSTR_mix, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} },
7676
{ MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 512} },
7777
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} },
@@ -91,7 +91,7 @@ static mp_obj_t audiofilters_filter_make_new(const mp_obj_type_t *type, size_t n
9191
}
9292

9393
audiofilters_filter_obj_t *self = mp_obj_malloc(audiofilters_filter_obj_t, &audiofilters_filter_type);
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);
94+
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);
9595

9696
return MP_OBJ_FROM_PTR(self);
9797
}
@@ -129,34 +129,34 @@ static mp_obj_t audiofilters_filter_obj___exit__(size_t n_args, const mp_obj_t *
129129
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiofilters_filter___exit___obj, 4, 4, audiofilters_filter_obj___exit__);
130130

131131

132-
//| filters: List[synthio.Biquad]
133-
//| """A list of normalized biquad filter objects 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."""
134134
//|
135-
static mp_obj_t audiofilters_filter_obj_get_filters(mp_obj_t self_in) {
135+
static mp_obj_t audiofilters_filter_obj_get_filter(mp_obj_t self_in) {
136136
audiofilters_filter_obj_t *self = MP_OBJ_TO_PTR(self_in);
137137
check_for_deinit(self);
138-
return common_hal_audiofilters_filter_get_filters(self);
138+
return common_hal_audiofilters_filter_get_filter(self);
139139
}
140-
MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_filter_get_filters_obj, audiofilters_filter_obj_get_filters);
140+
MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_filter_get_filter_obj, audiofilters_filter_obj_get_filter);
141141

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 };
142+
static mp_obj_t audiofilters_filter_obj_set_filter(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
143+
enum { ARG_filter };
144144
static const mp_arg_t allowed_args[] = {
145-
{ MP_QSTR_filters, MP_ARG_OBJ | MP_ARG_REQUIRED, {} },
145+
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_REQUIRED, {} },
146146
};
147147
audiofilters_filter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
148148
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
149149
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
150150

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

153153
return mp_const_none;
154154
}
155-
MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_set_filters_obj, 1, audiofilters_filter_obj_set_filters);
155+
MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_set_filter_obj, 1, audiofilters_filter_obj_set_filter);
156156

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);
157+
MP_PROPERTY_GETSET(audiofilters_filter_filter_obj,
158+
(mp_obj_t)&audiofilters_filter_get_filter_obj,
159+
(mp_obj_t)&audiofilters_filter_set_filter_obj);
160160

161161

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

246246
// Properties
247247
{ MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiofilters_filter_playing_obj) },
248-
{ MP_ROM_QSTR(MP_QSTR_filters), MP_ROM_PTR(&audiofilters_filter_filters_obj) },
248+
{ MP_ROM_QSTR(MP_QSTR_filter), MP_ROM_PTR(&audiofilters_filter_filter_obj) },
249249
{ MP_ROM_QSTR(MP_QSTR_mix), MP_ROM_PTR(&audiofilters_filter_mix_obj) },
250250
};
251251
static MP_DEFINE_CONST_DICT(audiofilters_filter_locals_dict, audiofilters_filter_locals_dict_table);

shared-bindings/audiofilters/Filter.h

+3-3
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 filters, mp_obj_t mix,
14+
mp_obj_t filter, 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_filters(audiofilters_filter_obj_t *self);
26-
void common_hal_audiofilters_filter_set_filters(audiofilters_filter_obj_t *self, mp_obj_t arg);
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);
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

+53-30
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 filters, mp_obj_t mix,
12+
mp_obj_t filter, 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,11 +60,10 @@ 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 (filters == MP_OBJ_NULL) {
64-
filters = mp_obj_new_list(0, NULL);
63+
if (filter == MP_OBJ_NULL) {
64+
filter = mp_const_none;
6565
}
66-
self->filters = filters;
67-
reset_filter_states(self);
66+
common_hal_audiofilters_filter_set_filter(self, filter);
6867

6968
// If we did not receive a BlockInput we need to create a default float value
7069
if (mix == MP_OBJ_NULL) {
@@ -91,32 +90,59 @@ void common_hal_audiofilters_filter_deinit(audiofilters_filter_obj_t *self) {
9190
}
9291

9392
void reset_filter_states(audiofilters_filter_obj_t *self) {
94-
self->filter_states_len = self->filters->len;
93+
self->filter_states_len = 0;
9594
self->filter_states = NULL;
9695

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-
}
96+
mp_obj_t *items;
97+
if (mp_obj_is_type(self->filter, (const mp_obj_type_t *)&synthio_biquad_type_obj)) {
98+
self->filter_states_len = 1;
99+
items = self->filter;
100+
} else if (mp_obj_is_tuple_compatible(self->filter)) {
101+
mp_obj_tuple_get(self->filter, &self->filter_states_len, &items);
102+
}
103+
104+
if (!self->filter_states_len) {
105+
return;
106+
}
107+
108+
self->filter_states = m_malloc(self->filter_states_len * sizeof(biquad_filter_state));
109+
if (self->filter_states == NULL) {
110+
common_hal_audiofilters_filter_deinit(self);
111+
m_malloc_fail(self->filter_states_len * sizeof(biquad_filter_state));
112+
}
103113

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);
114+
if (mp_obj_is_type(items, (const mp_obj_type_t *)&synthio_biquad_type_obj)) {
115+
synthio_biquad_filter_assign(&self->filter_states[0], items);
116+
} else {
117+
for (size_t i = 0; i < self->filter_states_len; i++) {
118+
synthio_biquad_filter_assign(&self->filter_states[i], items[i]);
110119
}
111120
}
112121
}
113122

114-
mp_obj_t common_hal_audiofilters_filter_get_filters(audiofilters_filter_obj_t *self) {
115-
return self->filters;
123+
mp_obj_t common_hal_audiofilters_filter_get_filter(audiofilters_filter_obj_t *self) {
124+
if (mp_obj_is_type(self->filter, (const mp_obj_type_t *)&synthio_biquad_type_obj) || mp_obj_is_tuple_compatible(self->filter)) {
125+
return self->filter;
126+
} else {
127+
return mp_const_none;
128+
}
116129
}
117130

118-
void common_hal_audiofilters_filter_set_filters(audiofilters_filter_obj_t *self, mp_obj_t arg) {
119-
self->filters = arg;
131+
void common_hal_audiofilters_filter_set_filter(audiofilters_filter_obj_t *self, mp_obj_t arg) {
132+
if (arg == mp_const_none || mp_obj_is_type(arg, (const mp_obj_type_t *)&synthio_biquad_type_obj)) {
133+
self->filter = arg;
134+
} else if (mp_obj_is_tuple_compatible(arg)) {
135+
mp_obj_tuple_t *tuple_obj = (mp_obj_tuple_t *)MP_OBJ_TO_PTR(arg);
136+
self->filter = mp_obj_new_tuple(tuple_obj->len, tuple_obj->items);
137+
} else if (mp_obj_is_type(arg, &mp_type_list)) {
138+
size_t list_len;
139+
mp_obj_t *list_items;
140+
mp_obj_list_get(arg, &list_len, &list_items);
141+
self->filter = mp_obj_new_tuple(list_len, list_items);
142+
} else {
143+
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);
144+
}
145+
120146
reset_filter_states(self);
121147
}
122148

@@ -148,8 +174,10 @@ void audiofilters_filter_reset_buffer(audiofilters_filter_obj_t *self,
148174
memset(self->buffer[1], 0, self->buffer_len);
149175
memset(self->filter_buffer, 0, SYNTHIO_MAX_DUR * sizeof(int32_t));
150176

151-
for (uint8_t i = 0; i < self->filter_states_len; i++) {
152-
synthio_biquad_filter_reset(&self->filter_states[i]);
177+
if (self->filter_states) {
178+
for (uint8_t i = 0; i < self->filter_states_len; i++) {
179+
synthio_biquad_filter_reset(&self->filter_states[i]);
180+
}
153181
}
154182
}
155183

@@ -233,11 +261,6 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
233261
channel = 0;
234262
}
235263

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-
241264
// get the effect values we need from the BlockInput. These may change at run time so you need to do bounds checking if required
242265
mp_float_t mix = MIN(1.0, MAX(synthio_block_slot_get(&self->mix), 0.0));
243266

@@ -277,7 +300,7 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
277300
int16_t *sample_src = (int16_t *)self->sample_remaining_buffer; // for 16-bit samples
278301
int8_t *sample_hsrc = (int8_t *)self->sample_remaining_buffer; // for 8-bit samples
279302

280-
if (mix <= 0.01 || !self->filter_states_len) { // if mix is zero pure sample only or no biquad filter objects are provided
303+
if (mix <= 0.01 || !self->filter_states) { // if mix is zero pure sample only or no biquad filter objects are provided
281304
for (uint32_t i = 0; i < n; i++) {
282305
if (MP_LIKELY(self->bits_per_sample == 16)) {
283306
word_buffer[i] = sample_src[i];

shared-module/audiofilters/Filter.h

+3-2
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,10 @@ extern const mp_obj_type_t audiofilters_filter_type;
1516

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

21-
uint8_t filter_states_len;
22+
size_t filter_states_len;
2223
biquad_filter_state *filter_states;
2324

2425
uint8_t bits_per_sample;

0 commit comments

Comments
 (0)