Skip to content

Commit 2975e22

Browse files
committed
Include synthio.Biquad methods within audiofilters.Filter.
1 parent 38779cd commit 2975e22

File tree

1 file changed

+115
-5
lines changed

1 file changed

+115
-5
lines changed

shared-bindings/audiofilters/Filter.c

Lines changed: 115 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
#include "py/runtime.h"
1616
#include "shared-bindings/util.h"
1717
#include "shared-module/synthio/block.h"
18-
19-
#define MIX_DEFAULT 1.0f
18+
#include "shared-bindings/synthio/Biquad.h"
2019

2120
//| class Filter:
2221
//| """A Filter effect"""
@@ -31,9 +30,8 @@
3130
//| samples_signed: bool = True,
3231
//| channel_count: int = 1,
3332
//| ) -> None:
34-
//| """Create a Filter effect where the original sample is processed through a biquad filter
35-
//| created by a synthio.Synthesizer object. This can be used to generate a low-pass,
36-
//| high-pass, or band-pass filter.
33+
//| """Create a Filter effect where the original sample is processed through a biquad filter.
34+
//| This can be used to generate a low-pass, high-pass, or band-pass filter.
3735
//|
3836
//| The mix parameter allows you to change how much of the unchanged sample passes through to
3937
//| the output to how much of the effect audio you hear as the output.
@@ -155,6 +153,115 @@ MP_PROPERTY_GETSET(audiofilters_filter_filter_obj,
155153
(mp_obj_t)&audiofilters_filter_set_filter_obj);
156154

157155

156+
//| def low_pass_filter(
157+
//| self, frequency: float, q_factor: float = 0.7071067811865475
158+
//| ) -> Biquad:
159+
//| """Construct a low-pass filter with the given parameters and update the `filter` property.
160+
//|
161+
//| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz
162+
//| of the filter.
163+
//|
164+
//| ``q_factor``, called ``Q`` in the cookbook. Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
165+
//| """
166+
167+
enum passfilter_arg_e { ARG_f0, ARG_Q };
168+
169+
// M_PI is not part of the math.h standard and may not be defined
170+
// And by defining our own we can ensure it uses the correct const format.
171+
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
172+
173+
static const mp_arg_t passfilter_properties[] = {
174+
{ MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
175+
{ MP_QSTR_Q, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL } },
176+
};
177+
178+
static mp_obj_t audiofilters_filter_obj_lpf(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
179+
audiofilters_filter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
180+
check_for_deinit(self);
181+
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
182+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
183+
184+
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
185+
mp_float_t Q =
186+
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
187+
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
188+
189+
mp_float_t w0 = f0 / self->sample_rate * 2 * MP_PI;
190+
191+
mp_obj_t biquad = common_hal_synthio_new_lpf(w0, Q);
192+
common_hal_audiofilters_filter_set_filter(self, biquad);
193+
return biquad;
194+
}
195+
196+
MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_lpf_obj, 1, audiofilters_filter_obj_lpf);
197+
198+
199+
//| def high_pass_filter(
200+
//| self, frequency: float, q_factor: float = 0.7071067811865475
201+
//| ) -> Biquad:
202+
//| """Construct a high-pass filter with the given parameters and update the `filter` property.
203+
//|
204+
//| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz
205+
//| of the filter.
206+
//|
207+
//| ``q_factor``, called ``Q`` in the cookbook. Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
208+
//| """
209+
210+
static mp_obj_t audiofilters_filter_obj_hpf(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
211+
audiofilters_filter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
212+
check_for_deinit(self);
213+
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
214+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
215+
216+
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
217+
mp_float_t Q =
218+
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
219+
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
220+
221+
mp_float_t w0 = f0 / self->sample_rate * 2 * MP_PI;
222+
223+
mp_obj_t biquad = common_hal_synthio_new_hpf(w0, Q);
224+
common_hal_audiofilters_filter_set_filter(self, biquad);
225+
return biquad;
226+
}
227+
228+
MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_hpf_obj, 1, audiofilters_filter_obj_hpf);
229+
230+
231+
//| def band_pass_filter(
232+
//| self, frequency: float, q_factor: float = 0.7071067811865475
233+
//| ) -> Biquad:
234+
//| """Construct a band-pass filter with the given parameters and update the `filter` property.
235+
//|
236+
//| ``frequency``, called f0 in the cookbook, is the center frequency in Hz
237+
//| of the filter.
238+
//|
239+
//| ``q_factor``, called ``Q`` in the cookbook. Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
240+
//|
241+
//| The coefficients are scaled such that the filter has a 0dB peak gain.
242+
//| """
243+
244+
static mp_obj_t audiofilters_filter_obj_bpf(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
245+
audiofilters_filter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
246+
check_for_deinit(self);
247+
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
248+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
249+
250+
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
251+
mp_float_t Q =
252+
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
253+
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
254+
255+
mp_float_t w0 = f0 / self->sample_rate * 2 * MP_PI;
256+
257+
mp_obj_t biquad = common_hal_synthio_new_bpf(w0, Q);
258+
common_hal_audiofilters_filter_set_filter(self, biquad);
259+
return biquad;
260+
}
261+
262+
MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_bpf_obj, 1, audiofilters_filter_obj_bpf);
263+
264+
158265
//| mix: synthio.BlockInput
159266
//| """The rate the filtered signal mix between 0 and 1 where 0 is only sample and 1 is all effect."""
160267
static mp_obj_t audiofilters_filter_obj_get_mix(mp_obj_t self_in) {
@@ -238,6 +345,9 @@ static const mp_rom_map_elem_t audiofilters_filter_locals_dict_table[] = {
238345
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiofilters_filter___exit___obj) },
239346
{ MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiofilters_filter_play_obj) },
240347
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiofilters_filter_stop_obj) },
348+
{ MP_ROM_QSTR(MP_QSTR_low_pass_filter), MP_ROM_PTR(&audiofilters_filter_lpf_obj) },
349+
{ MP_ROM_QSTR(MP_QSTR_high_pass_filter), MP_ROM_PTR(&audiofilters_filter_hpf_obj) },
350+
{ MP_ROM_QSTR(MP_QSTR_band_pass_filter), MP_ROM_PTR(&audiofilters_filter_bpf_obj) },
241351

242352
// Properties
243353
{ MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiofilters_filter_playing_obj) },

0 commit comments

Comments
 (0)