|
15 | 15 | #include "py/runtime.h" |
16 | 16 | #include "shared-bindings/util.h" |
17 | 17 | #include "shared-module/synthio/block.h" |
18 | | - |
19 | | -#define MIX_DEFAULT 1.0f |
| 18 | +#include "shared-bindings/synthio/Biquad.h" |
20 | 19 |
|
21 | 20 | //| class Filter: |
22 | 21 | //| """A Filter effect""" |
|
31 | 30 | //| samples_signed: bool = True, |
32 | 31 | //| channel_count: int = 1, |
33 | 32 | //| ) -> 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. |
37 | 35 | //| |
38 | 36 | //| The mix parameter allows you to change how much of the unchanged sample passes through to |
39 | 37 | //| 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, |
155 | 153 | (mp_obj_t)&audiofilters_filter_set_filter_obj); |
156 | 154 |
|
157 | 155 |
|
| 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 | + |
158 | 265 | //| mix: synthio.BlockInput |
159 | 266 | //| """The rate the filtered signal mix between 0 and 1 where 0 is only sample and 1 is all effect.""" |
160 | 267 | 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[] = { |
238 | 345 | { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiofilters_filter___exit___obj) }, |
239 | 346 | { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiofilters_filter_play_obj) }, |
240 | 347 | { 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) }, |
241 | 351 |
|
242 | 352 | // Properties |
243 | 353 | { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiofilters_filter_playing_obj) }, |
|
0 commit comments