Skip to content

Commit c408193

Browse files
authored
Merge pull request #8048 from jepler/synthio-biquad
Synthio: switch to per-note biquad filtering
2 parents b71f394 + a999e40 commit c408193

30 files changed

+5646
-5351
lines changed

ports/unix/variants/coverage/mpconfigvariant.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ SRC_BITMAP := \
4545
shared-bindings/synthio/MidiTrack.c \
4646
shared-bindings/synthio/LFO.c \
4747
shared-bindings/synthio/Note.c \
48+
shared-bindings/synthio/Biquad.c \
4849
shared-bindings/synthio/Synthesizer.c \
4950
shared-bindings/traceback/__init__.c \
5051
shared-bindings/util.c \
@@ -70,6 +71,7 @@ SRC_BITMAP := \
7071
shared-module/synthio/MidiTrack.c \
7172
shared-module/synthio/LFO.c \
7273
shared-module/synthio/Note.c \
74+
shared-module/synthio/Biquad.c \
7375
shared-module/synthio/Synthesizer.c \
7476
shared-module/traceback/__init__.c \
7577
shared-module/zlib/__init__.c \

py/circuitpy_defns.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ SRC_SHARED_MODULE_ALL = \
650650
struct/__init__.c \
651651
supervisor/__init__.c \
652652
supervisor/StatusBar.c \
653+
synthio/Biquad.c \
653654
synthio/LFO.c \
654655
synthio/Math.c \
655656
synthio/MidiTrack.c \

shared-bindings/synthio/Biquad.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 Artyom Skrobov
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <math.h>
28+
#include <string.h>
29+
30+
#include "py/enum.h"
31+
#include "py/mperrno.h"
32+
#include "py/obj.h"
33+
#include "py/objnamedtuple.h"
34+
#include "py/runtime.h"
35+
36+
#include "shared-bindings/synthio/__init__.h"
37+
#include "shared-bindings/synthio/LFO.h"
38+
#include "shared-bindings/synthio/Math.h"
39+
#include "shared-bindings/synthio/MidiTrack.h"
40+
#include "shared-bindings/synthio/Note.h"
41+
#include "shared-bindings/synthio/Synthesizer.h"
42+
43+
#include "shared-module/synthio/LFO.h"
44+
45+
#define default_attack_time (MICROPY_FLOAT_CONST(0.1))
46+
#define default_decay_time (MICROPY_FLOAT_CONST(0.05))
47+
#define default_release_time (MICROPY_FLOAT_CONST(0.2))
48+
#define default_attack_level (MICROPY_FLOAT_CONST(1.))
49+
#define default_sustain_level (MICROPY_FLOAT_CONST(0.8))
50+
51+
static const mp_arg_t biquad_properties[] = {
52+
{ MP_QSTR_a1, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
53+
{ MP_QSTR_a2, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
54+
{ MP_QSTR_b0, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
55+
{ MP_QSTR_b1, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
56+
{ MP_QSTR_b2, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
57+
};
58+
59+
//| class Biquad:
60+
//| def __init__(self, b0: float, b1: float, b2: float, a1: float, a2: float) -> None:
61+
//| """Construct a normalized biquad filter object.
62+
//|
63+
//| This implements the "direct form 1" biquad filter, where each coefficient
64+
//| has been pre-divided by a0.
65+
//|
66+
//| Biquad objects are usually constructed via one of the related methods on a `Synthesizer` object
67+
//| rather than directly from coefficients.
68+
//|
69+
//| https://github.com/WebAudio/Audio-EQ-Cookbook/blob/main/Audio-EQ-Cookbook.txt
70+
//| """
71+
//|
72+
STATIC mp_obj_t synthio_biquad_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
73+
mp_arg_val_t args[MP_ARRAY_SIZE(biquad_properties)];
74+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(biquad_properties), biquad_properties, args);
75+
76+
for (size_t i = 0; i < MP_ARRAY_SIZE(biquad_properties); i++) {
77+
args[i].u_obj = mp_obj_new_float(mp_arg_validate_type_float(args[i].u_obj, biquad_properties[i].qst));
78+
}
79+
80+
MP_STATIC_ASSERT(sizeof(mp_arg_val_t) == sizeof(mp_obj_t));
81+
return namedtuple_make_new(type_in, MP_ARRAY_SIZE(args), 0, &args[0].u_obj);
82+
}
83+
84+
const mp_obj_namedtuple_type_t synthio_biquad_type_obj = {
85+
.base = {
86+
.base = {
87+
.type = &mp_type_type
88+
},
89+
.flags = MP_TYPE_FLAG_EXTENDED,
90+
.name = MP_QSTR_Biquad,
91+
.print = namedtuple_print,
92+
.parent = &mp_type_tuple,
93+
.make_new = synthio_biquad_make_new,
94+
.attr = namedtuple_attr,
95+
MP_TYPE_EXTENDED_FIELDS(
96+
.unary_op = mp_obj_tuple_unary_op,
97+
.binary_op = mp_obj_tuple_binary_op,
98+
.subscr = mp_obj_tuple_subscr,
99+
.getiter = mp_obj_tuple_getiter,
100+
),
101+
},
102+
.n_fields = 5,
103+
.fields = {
104+
MP_QSTR_a1,
105+
MP_QSTR_a2,
106+
MP_QSTR_b0,
107+
MP_QSTR_b1,
108+
MP_QSTR_b2,
109+
},
110+
};

shared-bindings/synthio/Biquad.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
3+
#include "py/obj.h"
4+
#include "py/objnamedtuple.h"
5+
6+
extern const mp_obj_namedtuple_type_t synthio_biquad_type_obj;
7+
mp_obj_t common_hal_synthio_new_lpf(mp_float_t w0, mp_float_t Q);
8+
mp_obj_t common_hal_synthio_new_hpf(mp_float_t w0, mp_float_t Q);
9+
mp_obj_t common_hal_synthio_new_bpf(mp_float_t w0, mp_float_t Q);

shared-bindings/synthio/Note.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static const mp_arg_t note_properties[] = {
4141
{ MP_QSTR_bend, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
4242
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
4343
{ MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
44-
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1) } },
44+
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
4545
{ MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
4646
{ MP_QSTR_ring_bend, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
4747
{ MP_QSTR_ring_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
@@ -56,6 +56,7 @@ static const mp_arg_t note_properties[] = {
5656
//| envelope: Optional[Envelope] = None,
5757
//| amplitude: BlockInput = 0.0,
5858
//| bend: BlockInput = 0.0,
59+
//| filter: Optional[Biquad] = None,
5960
//| ring_frequency: float = 0.0,
6061
//| ring_bend: float = 0.0,
6162
//| ring_waveform: Optional[ReadableBuffer] = 0.0,
@@ -97,17 +98,21 @@ MP_PROPERTY_GETSET(synthio_note_frequency_obj,
9798
(mp_obj_t)&synthio_note_get_frequency_obj,
9899
(mp_obj_t)&synthio_note_set_frequency_obj);
99100

100-
//| filter: bool
101-
//| """True if the note should be processed via the synthesizer's FIR filter."""
101+
//| filter: Optional[Biquad]
102+
//| """If not None, the output of this Note is filtered according to the provided coefficients.
103+
//|
104+
//| Construct an appropriate filter by calling a filter-making method on the
105+
//| `Synthesizer` object where you plan to play the note, as filter coefficients depend
106+
//| on the sample rate"""
102107
STATIC mp_obj_t synthio_note_get_filter(mp_obj_t self_in) {
103108
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
104-
return mp_obj_new_bool(common_hal_synthio_note_get_filter(self));
109+
return common_hal_synthio_note_get_filter_obj(self);
105110
}
106111
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_filter_obj, synthio_note_get_filter);
107112

108113
STATIC mp_obj_t synthio_note_set_filter(mp_obj_t self_in, mp_obj_t arg) {
109114
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
110-
common_hal_synthio_note_set_filter(self, mp_obj_is_true(arg));
115+
common_hal_synthio_note_set_filter(self, arg);
111116
return mp_const_none;
112117
}
113118
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_filter_obj, synthio_note_set_filter);

shared-bindings/synthio/Note.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ typedef enum synthio_bend_mode_e synthio_bend_mode_t;
99
mp_float_t common_hal_synthio_note_get_frequency(synthio_note_obj_t *self);
1010
void common_hal_synthio_note_set_frequency(synthio_note_obj_t *self, mp_float_t value);
1111

12-
bool common_hal_synthio_note_get_filter(synthio_note_obj_t *self);
13-
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, bool value);
12+
mp_obj_t common_hal_synthio_note_get_filter_obj(synthio_note_obj_t *self);
13+
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, mp_obj_t biquad);
1414

1515
mp_obj_t common_hal_synthio_note_get_panning(synthio_note_obj_t *self);
1616
void common_hal_synthio_note_set_panning(synthio_note_obj_t *self, mp_obj_t value);

shared-bindings/synthio/Synthesizer.c

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "py/objproperty.h"
3333
#include "py/runtime.h"
3434
#include "shared-bindings/util.h"
35+
#include "shared-bindings/synthio/Biquad.h"
3536
#include "shared-bindings/synthio/Synthesizer.h"
3637
#include "shared-bindings/synthio/LFO.h"
3738
#include "shared-bindings/synthio/__init__.h"
@@ -52,7 +53,6 @@
5253
//| channel_count: int = 1,
5354
//| waveform: Optional[ReadableBuffer] = None,
5455
//| envelope: Optional[Envelope] = None,
55-
//| filter: Optional[ReadableBuffer] = None,
5656
//| ) -> None:
5757
//| """Create a synthesizer object.
5858
//|
@@ -65,17 +65,15 @@
6565
//| :param int sample_rate: The desired playback sample rate; higher sample rate requires more memory
6666
//| :param int channel_count: The number of output channels (1=mono, 2=stereo)
6767
//| :param ReadableBuffer waveform: A single-cycle waveform. Default is a 50% duty cycle square wave. If specified, must be a ReadableBuffer of type 'h' (signed 16 bit)
68-
//| :param ReadableBuffer filter: Coefficients of an FIR filter to apply to notes with ``filter=True``. If specified, must be a ReadableBuffer of type 'h' (signed 16 bit)
6968
//| :param Optional[Envelope] envelope: An object that defines the loudness of a note over time. The default envelope, `None` provides no ramping, voices turn instantly on and off.
7069
//| """
7170
STATIC mp_obj_t synthio_synthesizer_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_sample_rate, ARG_channel_count, ARG_waveform, ARG_envelope, ARG_filter };
71+
enum { ARG_sample_rate, ARG_channel_count, ARG_waveform, ARG_envelope };
7372
static const mp_arg_t allowed_args[] = {
7473
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} },
7574
{ MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} },
7675
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
7776
{ MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
78-
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
7977
};
8078
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
8179
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
@@ -87,7 +85,6 @@ STATIC mp_obj_t synthio_synthesizer_make_new(const mp_obj_type_t *type, size_t n
8785
args[ARG_sample_rate].u_int,
8886
args[ARG_channel_count].u_int,
8987
args[ARG_waveform].u_obj,
90-
args[ARG_filter].u_obj,
9188
args[ARG_envelope].u_obj);
9289

9390
return MP_OBJ_FROM_PTR(self);
@@ -292,6 +289,114 @@ MP_PROPERTY_GETTER(synthio_synthesizer_blocks_obj,
292289
//| """Maximum polyphony of the synthesizer (read-only class property)"""
293290
//|
294291

292+
//| def low_pass_filter(cls, frequency: float, q_factor: float = 0.7071067811865475) -> Biquad:
293+
//| """Construct a low-pass filter with the given parameters.
294+
//|
295+
//| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz
296+
//| of the filter.
297+
//|
298+
//| ``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.
299+
//| """
300+
301+
enum passfilter_arg_e { ARG_f0, ARG_Q };
302+
303+
// M_PI is not part of the math.h standard and may not be defined
304+
// And by defining our own we can ensure it uses the correct const format.
305+
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
306+
307+
static const mp_arg_t passfilter_properties[] = {
308+
{ MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
309+
{ MP_QSTR_Q, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL } },
310+
};
311+
312+
STATIC mp_obj_t synthio_synthesizer_lpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) {
313+
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
314+
315+
mp_obj_t self_in = pos_args[0];
316+
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
317+
318+
mp_arg_parse_all(n_pos - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
319+
320+
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
321+
mp_float_t Q =
322+
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
323+
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
324+
325+
mp_float_t w0 = f0 / self->synth.sample_rate * 2 * MP_PI;
326+
327+
return common_hal_synthio_new_lpf(w0, Q);
328+
329+
}
330+
331+
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_lpf_fun_obj, 1, synthio_synthesizer_lpf);
332+
333+
//| def high_pass_filter(
334+
//| cls, frequency: float, q_factor: float = 0.7071067811865475
335+
//| ) -> Biquad:
336+
//| """Construct a high-pass filter with the given parameters.
337+
//|
338+
//| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz
339+
//| of the filter.
340+
//|
341+
//| ``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.
342+
//| """
343+
344+
STATIC mp_obj_t synthio_synthesizer_hpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) {
345+
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
346+
347+
mp_obj_t self_in = pos_args[0];
348+
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
349+
350+
mp_arg_parse_all(n_pos - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
351+
352+
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
353+
mp_float_t Q =
354+
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
355+
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
356+
357+
mp_float_t w0 = f0 / self->synth.sample_rate * 2 * MP_PI;
358+
359+
return common_hal_synthio_new_hpf(w0, Q);
360+
361+
}
362+
363+
//| def band_pass_filter(
364+
//| cls, frequency: float, q_factor: float = 0.7071067811865475
365+
//| ) -> Biquad:
366+
//| """Construct a band-pass filter with the given parameters.
367+
//|
368+
//| ``frequency``, called f0 in the cookbook, is the center frequency in Hz
369+
//| of the filter.
370+
//|
371+
//| ``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.
372+
//|
373+
//| The coefficients are scaled such that the filter has a 0dB peak gain.
374+
//| """
375+
//|
376+
377+
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_hpf_fun_obj, 1, synthio_synthesizer_hpf);
378+
379+
STATIC mp_obj_t synthio_synthesizer_bpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) {
380+
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
381+
382+
mp_obj_t self_in = pos_args[0];
383+
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
384+
385+
mp_arg_parse_all(n_pos - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
386+
387+
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
388+
mp_float_t Q =
389+
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
390+
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
391+
392+
mp_float_t w0 = f0 / self->synth.sample_rate * 2 * MP_PI;
393+
394+
return common_hal_synthio_new_bpf(w0, Q);
395+
396+
}
397+
398+
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_bpf_fun_obj, 1, synthio_synthesizer_bpf);
399+
295400
STATIC const mp_rom_map_elem_t synthio_synthesizer_locals_dict_table[] = {
296401
// Methods
297402
{ MP_ROM_QSTR(MP_QSTR_press), MP_ROM_PTR(&synthio_synthesizer_press_obj) },
@@ -304,6 +409,9 @@ STATIC const mp_rom_map_elem_t synthio_synthesizer_locals_dict_table[] = {
304409
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
305410
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&synthio_synthesizer___exit___obj) },
306411

412+
{ MP_ROM_QSTR(MP_QSTR_low_pass_filter), MP_ROM_PTR(&synthio_synthesizer_lpf_fun_obj) },
413+
{ MP_ROM_QSTR(MP_QSTR_high_pass_filter), MP_ROM_PTR(&synthio_synthesizer_hpf_fun_obj) },
414+
{ MP_ROM_QSTR(MP_QSTR_band_pass_filter), MP_ROM_PTR(&synthio_synthesizer_bpf_fun_obj) },
307415
// Properties
308416
{ MP_ROM_QSTR(MP_QSTR_envelope), MP_ROM_PTR(&synthio_synthesizer_envelope_obj) },
309417
{ MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&synthio_synthesizer_sample_rate_obj) },

shared-bindings/synthio/Synthesizer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
extern const mp_obj_type_t synthio_synthesizer_type;
3333

3434
void common_hal_synthio_synthesizer_construct(synthio_synthesizer_obj_t *self,
35-
uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj,
35+
uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj,
3636
mp_obj_t envelope_obj);
3737
void common_hal_synthio_synthesizer_deinit(synthio_synthesizer_obj_t *self);
3838
bool common_hal_synthio_synthesizer_deinited(synthio_synthesizer_obj_t *self);

shared-bindings/synthio/__init__.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "extmod/vfs_posix.h"
3737

3838
#include "shared-bindings/synthio/__init__.h"
39+
#include "shared-bindings/synthio/Biquad.h"
3940
#include "shared-bindings/synthio/LFO.h"
4041
#include "shared-bindings/synthio/Math.h"
4142
#include "shared-bindings/synthio/MidiTrack.h"
@@ -310,6 +311,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR(synthio_lfo_tick_obj, 1, synthio_lfo_tick);
310311

311312
STATIC const mp_rom_map_elem_t synthio_module_globals_table[] = {
312313
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_synthio) },
314+
{ MP_ROM_QSTR(MP_QSTR_Biquad), MP_ROM_PTR(&synthio_biquad_type_obj) },
313315
{ MP_ROM_QSTR(MP_QSTR_Math), MP_ROM_PTR(&synthio_math_type) },
314316
{ MP_ROM_QSTR(MP_QSTR_MathOperation), MP_ROM_PTR(&synthio_math_operation_type) },
315317
{ MP_ROM_QSTR(MP_QSTR_MidiTrack), MP_ROM_PTR(&synthio_miditrack_type) },

0 commit comments

Comments
 (0)