Skip to content

Commit 3f2c1e0

Browse files
authored
Merge pull request #9804 from jepler/synthio-blockbiquad-morefilters
Add BlockBiquad shelf & peaking, AudioFilter support
2 parents 886d225 + ad87adb commit 3f2c1e0

File tree

19 files changed

+102676
-92
lines changed

19 files changed

+102676
-92
lines changed

ports/atmel-samd/boards/pybadge/mpconfigboard.mk

+10
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,13 @@ CIRCUITPY_SPITARGET = 0
2222
CIRCUITPY_STAGE = 1
2323

2424
FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-stage/pybadge
25+
26+
# We don't have room for the fonts for terminalio for certain languages,
27+
# so turn off terminalio, and if it's off and displayio is on,
28+
# force a clean build.
29+
# Note that we cannot test $(CIRCUITPY_DISPLAYIO) directly with an
30+
# ifeq, because it's not set yet.
31+
ifneq (,$(filter $(TRANSLATION),ja ko ru))
32+
CIRCUITPY_TERMINALIO = 0
33+
RELEASE_NEEDS_CLEAN_BUILD = $(CIRCUITPY_DISPLAYIO)
34+
endif

ports/atmel-samd/boards/pygamer/mpconfigboard.mk

+10
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,13 @@ CIRCUITPY_SPITARGET = 0
2222
CIRCUITPY_STAGE = 1
2323

2424
FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-stage/pygamer
25+
26+
# We don't have room for the fonts for terminalio for certain languages,
27+
# so turn off terminalio, and if it's off and displayio is on,
28+
# force a clean build.
29+
# Note that we cannot test $(CIRCUITPY_DISPLAYIO) directly with an
30+
# ifeq, because it's not set yet.
31+
ifneq (,$(filter $(TRANSLATION),ja ko ru))
32+
CIRCUITPY_TERMINALIO = 0
33+
RELEASE_NEEDS_CLEAN_BUILD = $(CIRCUITPY_DISPLAYIO)
34+
endif

shared-bindings/audiofilters/Filter.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
//|
2525
//| def __init__(
2626
//| self,
27-
//| filter: Optional[synthio.Biquad | Tuple[synthio.Biquad]] = None,
27+
//| filter: Optional[synthio.AnyBiquad | Tuple[synthio.AnyBiquad]] = None,
2828
//| mix: synthio.BlockInput = 1.0,
2929
//| buffer_size: int = 512,
3030
//| sample_rate: int = 8000,
@@ -39,7 +39,7 @@
3939
//| The mix parameter allows you to change how much of the unchanged sample passes through to
4040
//| the output to how much of the effect audio you hear as the output.
4141
//|
42-
//| :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.
42+
//| :param Optional[synthio.AnyBiquad|Tuple[synthio.AnyBiquad]] 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.
4343
//| :param synthio.BlockInput mix: The mix as a ratio of the sample (0.0) to the effect (1.0).
4444
//| :param int buffer_size: The total size in bytes of each of the two playback buffers to use
4545
//| :param int sample_rate: The sample rate to be used
@@ -73,7 +73,7 @@
7373
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) {
7474
enum { ARG_filter, ARG_mix, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, };
7575
static const mp_arg_t allowed_args[] = {
76-
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} },
76+
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
7777
{ MP_QSTR_mix, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1)} },
7878
{ MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 512} },
7979
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} },
@@ -127,7 +127,7 @@ static void check_for_deinit(audiofilters_filter_obj_t *self) {
127127
// Provided by context manager helper.
128128

129129

130-
//| filter: synthio.Biquad | Tuple[synthio.Biquad] | None
130+
//| filter: synthio.AnyBiquad | Tuple[synthio.AnyBiquad] | None
131131
//| """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."""
132132
//|
133133
static mp_obj_t audiofilters_filter_obj_get_filter(mp_obj_t self_in) {

shared-bindings/synthio/Biquad.c

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ static const mp_arg_t biquad_properties[] = {
4747
//| rather than directly from coefficients.
4848
//|
4949
//| https://github.com/WebAudio/Audio-EQ-Cookbook/blob/main/Audio-EQ-Cookbook.txt
50+
//|
51+
//| .. note:: This is deprecated in ``9.x.x`` and will be removed in ``10.0.0``. Use `BlockBiquad` objects instead.
5052
//| """
5153
//|
5254
//|

shared-bindings/synthio/BlockBiquad.c

+52-3
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,31 @@
2121
//| """A band-pass filter"""
2222
//| NOTCH: FilterMode
2323
//| """A notch filter"""
24+
//| LOW_SHELF: FilterMode
25+
//| """A low shelf filter"""
26+
//| HIGH_SHELF: FilterMode
27+
//| """A high shelf filter"""
28+
//| PEAKING_EQ: FilterMode
29+
//| """A peaking equalizer filter"""
2430
//|
2531
//|
2632

2733
MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, LOW_PASS, SYNTHIO_LOW_PASS);
2834
MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, HIGH_PASS, SYNTHIO_HIGH_PASS);
2935
MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, BAND_PASS, SYNTHIO_BAND_PASS);
3036
MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, NOTCH, SYNTHIO_NOTCH);
37+
MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, LOW_SHELF, SYNTHIO_LOW_SHELF);
38+
MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, HIGH_SHELF, SYNTHIO_HIGH_SHELF);
39+
MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, PEAKING_EQ, SYNTHIO_PEAKING_EQ);
3140

3241
MAKE_ENUM_MAP(synthio_filter_mode) {
3342
MAKE_ENUM_MAP_ENTRY(mode, LOW_PASS),
3443
MAKE_ENUM_MAP_ENTRY(mode, HIGH_PASS),
3544
MAKE_ENUM_MAP_ENTRY(mode, BAND_PASS),
3645
MAKE_ENUM_MAP_ENTRY(mode, NOTCH),
46+
MAKE_ENUM_MAP_ENTRY(mode, LOW_SHELF),
47+
MAKE_ENUM_MAP_ENTRY(mode, HIGH_SHELF),
48+
MAKE_ENUM_MAP_ENTRY(mode, PEAKING_EQ),
3749
};
3850

3951
static MP_DEFINE_CONST_DICT(synthio_filter_mode_locals_dict, synthio_filter_mode_locals_table);
@@ -52,8 +64,17 @@ static synthio_filter_mode validate_synthio_filter_mode(mp_obj_t obj, qstr arg_n
5264
//| mode: FilterMode,
5365
//| frequency: BlockInput,
5466
//| Q: BlockInput = 0.7071067811865475,
67+
//| A: BlockInput = None,
5568
//| ) -> None:
56-
//| """Construct a biquad filter object with dynamic center frequency & q factor
69+
//| """Construct a biquad filter object with given settings.
70+
//|
71+
//| ``frequency`` gives the center frequency or corner frequency of the filter,
72+
//| depending on the mode.
73+
//|
74+
//| ``Q`` gives the gain or sharpness of the filter.
75+
//|
76+
//| ``A`` controls the gain of peaking and shelving filters according to the
77+
//| formula ``A = 10^(dBgain/40)``. For other filter types it is ignored.
5778
//|
5879
//| Since ``frequency`` and ``Q`` are `BlockInput` objects, they can
5980
//| be varied dynamically. Internally, this is evaluated as "direct form 1"
@@ -70,6 +91,7 @@ static const mp_arg_t block_biquad_properties[] = {
7091
{ MP_QSTR_mode, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL } },
7192
{ MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL } },
7293
{ MP_QSTR_Q, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL } },
94+
{ MP_QSTR_A, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE } },
7395
};
7496

7597
static mp_obj_t synthio_block_biquad_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
@@ -83,7 +105,9 @@ static mp_obj_t synthio_block_biquad_make_new(const mp_obj_type_t *type_in, size
83105
}
84106

85107
synthio_filter_mode mode = validate_synthio_filter_mode(args[ARG_mode].u_obj, MP_QSTR_mode);
86-
return common_hal_synthio_block_biquad_new(mode, args[ARG_frequency].u_obj, args[ARG_Q].u_obj);
108+
mp_obj_t result = common_hal_synthio_block_biquad_new(mode);
109+
properties_construct_helper(result, block_biquad_properties + 1, args + 1, MP_ARRAY_SIZE(block_biquad_properties) - 1);
110+
return result;
87111
}
88112

89113
//|
@@ -122,7 +146,6 @@ MP_PROPERTY_GETSET(synthio_block_biquad_frequency_obj,
122146
//| Q: BlockInput
123147
//| """The sharpness (Q) of the filter"""
124148
//|
125-
//|
126149
static mp_obj_t synthio_block_biquad_get_Q(mp_obj_t self_in) {
127150
synthio_block_biquad_t *self = MP_OBJ_TO_PTR(self_in);
128151
return common_hal_synthio_block_biquad_get_Q(self);
@@ -139,10 +162,36 @@ MP_PROPERTY_GETSET(synthio_block_biquad_Q_obj,
139162
(mp_obj_t)&synthio_block_biquad_get_Q_obj,
140163
(mp_obj_t)&synthio_block_biquad_set_Q_obj);
141164

165+
//|
166+
//| A: BlockInput
167+
//| """The gain (A) of the filter
168+
//|
169+
//| This setting only has an effect for peaking and shelving EQ filters. It is related
170+
//| to the filter gain according to the formula ``A = 10^(dBgain/40)``.
171+
//| """
172+
//|
173+
//|
174+
static mp_obj_t synthio_block_biquad_get_A(mp_obj_t self_in) {
175+
synthio_block_biquad_t *self = MP_OBJ_TO_PTR(self_in);
176+
return common_hal_synthio_block_biquad_get_A(self);
177+
}
178+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_block_biquad_get_A_obj, synthio_block_biquad_get_A);
179+
180+
static mp_obj_t synthio_block_biquad_set_A(mp_obj_t self_in, mp_obj_t arg) {
181+
synthio_block_biquad_t *self = MP_OBJ_TO_PTR(self_in);
182+
common_hal_synthio_block_biquad_set_A(self, arg);
183+
return mp_const_none;
184+
}
185+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_block_biquad_set_A_obj, synthio_block_biquad_set_A);
186+
MP_PROPERTY_GETSET(synthio_block_biquad_A_obj,
187+
(mp_obj_t)&synthio_block_biquad_get_A_obj,
188+
(mp_obj_t)&synthio_block_biquad_set_A_obj);
189+
142190
static const mp_rom_map_elem_t synthio_block_biquad_locals_dict_table[] = {
143191
{ MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&synthio_block_biquad_mode_obj) },
144192
{ MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&synthio_block_biquad_frequency_obj) },
145193
{ MP_ROM_QSTR(MP_QSTR_Q), MP_ROM_PTR(&synthio_block_biquad_Q_obj) },
194+
{ MP_ROM_QSTR(MP_QSTR_A), MP_ROM_PTR(&synthio_block_biquad_A_obj) },
146195
};
147196
static MP_DEFINE_CONST_DICT(synthio_block_biquad_locals_dict, synthio_block_biquad_locals_dict_table);
148197

shared-bindings/synthio/BlockBiquad.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@ extern const mp_obj_type_t synthio_filter_mode_type;
1313
typedef struct synthio_block_biquad synthio_block_biquad_t;
1414

1515
typedef enum {
16-
SYNTHIO_LOW_PASS, SYNTHIO_HIGH_PASS, SYNTHIO_BAND_PASS, SYNTHIO_NOTCH
16+
SYNTHIO_LOW_PASS, SYNTHIO_HIGH_PASS, SYNTHIO_BAND_PASS, SYNTHIO_NOTCH,
17+
// filters beyond this line use the "A" parameter (in addition to f0 and Q)
18+
SYNTHIO_PEAKING_EQ, SYNTHIO_LOW_SHELF, SYNTHIO_HIGH_SHELF
1719
} synthio_filter_mode;
1820

1921

22+
mp_obj_t common_hal_synthio_block_biquad_get_A(synthio_block_biquad_t *self);
23+
void common_hal_synthio_block_biquad_set_A(synthio_block_biquad_t *self, mp_obj_t A);
24+
2025
mp_obj_t common_hal_synthio_block_biquad_get_Q(synthio_block_biquad_t *self);
2126
void common_hal_synthio_block_biquad_set_Q(synthio_block_biquad_t *self, mp_obj_t Q);
2227

@@ -25,4 +30,4 @@ void common_hal_synthio_block_biquad_set_frequency(synthio_block_biquad_t *self,
2530

2631
synthio_filter_mode common_hal_synthio_block_biquad_get_mode(synthio_block_biquad_t *self);
2732

28-
mp_obj_t common_hal_synthio_block_biquad_new(synthio_filter_mode mode, mp_obj_t frequency, mp_obj_t Q);
33+
mp_obj_t common_hal_synthio_block_biquad_new(synthio_filter_mode mode);

shared-bindings/synthio/LFO.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static const uint16_t triangle[] = {0, 32767, 0, -32767};
3434
//| including indirectly via a `Note` or another intermediate LFO.
3535
//|
3636
//| Using the same LFO as an input to multiple other LFOs or Notes is OK, but
37-
//| the result if an LFO is tied to multiple Synthtesizer objects is undefined.
37+
//| the result if an LFO is tied to multiple `Synthesizer` objects is undefined.
3838
//|
3939
//| In the current implementation, LFOs are updated every 256 samples. This
4040
//| should be considered an implementation detail, though it affects how LFOs

shared-bindings/synthio/Math.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ MAKE_ENUM_TYPE(synthio, MathOperation, synthio_math_operation,
123123
//| including indirectly via a `Note` or another intermediate Math.
124124
//|
125125
//| Using the same Math as an input to multiple other Maths or Notes is OK, but
126-
//| the result if an Math is tied to multiple Synthtesizer objects is undefined.
126+
//| the result if an Math is tied to multiple `Synthesizer` objects is undefined.
127127
//|
128128
//| In the current implementation, Maths are updated every 256 samples. This
129129
//| should be considered an implementation detail.

shared-bindings/synthio/Note.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static const mp_arg_t note_properties[] = {
4242
//| envelope: Optional[Envelope] = None,
4343
//| amplitude: BlockInput = 1.0,
4444
//| bend: BlockInput = 0.0,
45-
//| filter: Optional[Biquad] = None,
45+
//| filter: Optional[AnyBiquad] = None,
4646
//| ring_frequency: float = 0.0,
4747
//| ring_bend: float = 0.0,
4848
//| ring_waveform: Optional[ReadableBuffer] = None,
@@ -86,7 +86,7 @@ MP_PROPERTY_GETSET(synthio_note_frequency_obj,
8686
(mp_obj_t)&synthio_note_get_frequency_obj,
8787
(mp_obj_t)&synthio_note_set_frequency_obj);
8888

89-
//| filter: Optional[Biquad]
89+
//| filter: Optional[AnyBiquad]
9090
//| """If not None, the output of this Note is filtered according to the provided coefficients.
9191
//|
9292
//| Construct an appropriate filter by calling a filter-making method on the

shared-bindings/synthio/Synthesizer.c

+6
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ MP_PROPERTY_GETTER(synthio_synthesizer_blocks_obj,
290290
//| of the filter.
291291
//|
292292
//| ``Q`` controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
293+
//|
294+
//| .. note:: This is deprecated in ``9.x.x`` and will be removed in ``10.0.0``. Use `BlockBiquad` objects instead.
293295
//| """
294296
//|
295297

@@ -332,6 +334,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_lpf_fun_obj, 1, synthio_synthesiz
332334
//| of the filter.
333335
//|
334336
//| ``Q`` controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
337+
//|
338+
//| .. note:: This is deprecated in ``9.x.x`` and will be removed in ``10.0.0``. Use `BlockBiquad` objects instead.
335339
//| """
336340
//|
337341

@@ -363,6 +367,8 @@ static mp_obj_t synthio_synthesizer_hpf(size_t n_pos, const mp_obj_t *pos_args,
363367
//| ``Q`` Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
364368
//|
365369
//| The coefficients are scaled such that the filter has a 0dB peak gain.
370+
//|
371+
//| .. note:: This is deprecated in ``9.x.x`` and will be removed in ``10.0.0``. Use `BlockBiquad` objects instead.
366372
//| """
367373
//|
368374
//|

shared-bindings/synthio/__init__.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@
3232
//| At least 2 simultaneous notes are supported. samd5x, mimxrt10xx and rp2040 platforms support up to 12 notes.
3333
//| """
3434
//|
35-
//|
3635

36+
//| AnyBiquad = Union["Biquad", "BlockBiquad"]
37+
//|
38+
//|
3739
//| class EnvelopeState:
3840
//| ATTACK: EnvelopeState
3941
//| """The note is in its attack phase"""

0 commit comments

Comments
 (0)