Skip to content

Commit 3914381

Browse files
committed
synthio: Finish implementing Math blocks
1 parent 5de4d19 commit 3914381

14 files changed

Lines changed: 833 additions & 75 deletions

File tree

ports/unix/variants/coverage/mpconfigvariant.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ SRC_BITMAP := \
4141
shared-bindings/rainbowio/__init__.c \
4242
shared-bindings/struct/__init__.c \
4343
shared-bindings/synthio/__init__.c \
44+
shared-bindings/synthio/Math.c \
4445
shared-bindings/synthio/MidiTrack.c \
4546
shared-bindings/synthio/LFO.c \
4647
shared-bindings/synthio/Note.c \
@@ -65,6 +66,7 @@ SRC_BITMAP := \
6566
shared-module/rainbowio/__init__.c \
6667
shared-module/struct/__init__.c \
6768
shared-module/synthio/__init__.c \
69+
shared-module/synthio/Math.c \
6870
shared-module/synthio/MidiTrack.c \
6971
shared-module/synthio/LFO.c \
7072
shared-module/synthio/Note.c \

py/circuitpy_defns.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ SRC_SHARED_MODULE_ALL = \
651651
supervisor/__init__.c \
652652
supervisor/StatusBar.c \
653653
synthio/LFO.c \
654+
synthio/Math.c \
654655
synthio/MidiTrack.c \
655656
synthio/Note.c \
656657
synthio/Synthesizer.c \

shared-bindings/synthio/LFO.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
#include "shared-module/synthio/LFO.h"
3535

3636
//| class LFO:
37-
//| """A low-frequency oscillator
37+
//| """A low-frequency oscillator block
3838
//|
3939
//| Every `rate` seconds, the output of the LFO cycles through its `waveform`.
4040
//| The output at any particular moment is ``waveform[idx] * scale + offset``.

shared-bindings/synthio/Math.c

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/*
2+
* This file is part of the Micro Python project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 Artyom Skrobov
7+
* Copyright (c) 2023 Jeff Epler for Adafruit Industries
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in
17+
* all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
#include "py/obj.h"
29+
#include "py/objproperty.h"
30+
#include "py/proto.h"
31+
#include "py/runtime.h"
32+
#include "shared-bindings/util.h"
33+
#include "shared-bindings/synthio/Math.h"
34+
#include "shared-module/synthio/Math.h"
35+
36+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, SUM, OP_SUM);
37+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, ADD_SUB, OP_ADD_SUB);
38+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, PRODUCT, OP_PRODUCT);
39+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, MUL_DIV, OP_MUL_DIV);
40+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, SCALE_OFFSET, OP_SCALE_OFFSET);
41+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, OFFSET_SCALE, OP_OFFSET_SCALE);
42+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, LERP, OP_LERP);
43+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, CONSTRAINED_LERP, OP_CONSTRAINED_LERP);
44+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, DIV_ADD, OP_DIV_ADD);
45+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, ADD_DIV, OP_ADD_DIV);
46+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, MID, OP_MID);
47+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, MAX, OP_MAX);
48+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, MIN, OP_MIN);
49+
MAKE_ENUM_VALUE(synthio_math_operation_type, math_op, ABS, OP_ABS);
50+
51+
//| class MathOperation:
52+
//| """Operation for a Math block"""
53+
//|
54+
//| SUM: "MathOperation"
55+
//| """Computes ``a+b+c``. For 2-input sum, set one argument to ``0.0``. To hold a control value for multiple subscribers, set two arguments to ``0.0``."""
56+
//|
57+
//| ADD_SUB: "MathOperation"
58+
//| """Computes ``a+b-c``. For 2-input subtraction, set ``b`` to ``0.0``."""
59+
//|
60+
//| PRODUCT: "MathOperation"
61+
//| """Computes ``a*b*c``. For 2-input product, set one argument to ``1.0``."""
62+
//|
63+
//| MUL_DIV: "MathOperation"
64+
//| """Computes ``a*b/c``. If ``c`` is zero, the output is ``1.0``."""
65+
//|
66+
//| SCALE_OFFSET: "MathOperation"
67+
//| """Computes ``(a*b)+c``."""
68+
//|
69+
//| OFFSET_SCALE: "MathOperation"
70+
//| """Computes ``(a+b)*c``. For 2-input multiplication, set ``b`` to 0."""
71+
//|
72+
//| LERP: "MathOperation"
73+
//| """Computes ``a * (1-c) + b * c``."""
74+
//|
75+
//| CONSTRAINED_LERP: "MathOperation"
76+
//| """Computes ``a * (1-c') + b * c'``, where ``c'`` is constrained to be between ``0.0`` and ``1.0``."""
77+
//|
78+
//| DIV_ADD: "MathOperation"
79+
//| """Computes ``a/b+c``. If ``b`` is zero, the output is ``c``."""
80+
//|
81+
//| ADD_DIV: "MathOperation"
82+
//| """Computes ``(a+b)/c``. For 2-input product, set ``b`` to ``0.0``."""
83+
//|
84+
//| MID: "MathOperation"
85+
//| """Returns the middle of the 3 input values."""
86+
//|
87+
//| MAX: "MathOperation"
88+
//| """Returns the biggest of the 3 input values."""
89+
//|
90+
//| MIN: "MathOperation"
91+
//| """Returns the smallest of the 3 input values."""
92+
//|
93+
//| ABS: "MathOperation"
94+
//| """Returns the absolute value of ``a``."""
95+
//|
96+
MAKE_ENUM_MAP(synthio_math_operation) {
97+
MAKE_ENUM_MAP_ENTRY(math_op, SUM),
98+
MAKE_ENUM_MAP_ENTRY(math_op, ADD_SUB),
99+
MAKE_ENUM_MAP_ENTRY(math_op, PRODUCT),
100+
MAKE_ENUM_MAP_ENTRY(math_op, MUL_DIV),
101+
MAKE_ENUM_MAP_ENTRY(math_op, SCALE_OFFSET),
102+
MAKE_ENUM_MAP_ENTRY(math_op, OFFSET_SCALE),
103+
MAKE_ENUM_MAP_ENTRY(math_op, LERP),
104+
MAKE_ENUM_MAP_ENTRY(math_op, CONSTRAINED_LERP),
105+
MAKE_ENUM_MAP_ENTRY(math_op, DIV_ADD),
106+
MAKE_ENUM_MAP_ENTRY(math_op, ADD_DIV),
107+
MAKE_ENUM_MAP_ENTRY(math_op, MID),
108+
MAKE_ENUM_MAP_ENTRY(math_op, MAX),
109+
MAKE_ENUM_MAP_ENTRY(math_op, MIN),
110+
MAKE_ENUM_MAP_ENTRY(math_op, ABS),
111+
};
112+
113+
STATIC MP_DEFINE_CONST_DICT(synthio_math_operation_locals_dict, synthio_math_operation_locals_table);
114+
MAKE_PRINTER(synthio, synthio_math_operation);
115+
MAKE_ENUM_TYPE(synthio, MathOperation, synthio_math_operation);
116+
117+
//| class Math:
118+
//| """An arithmetic block
119+
//|
120+
//| Performs an arithmetic operation on up to 3 inputs. See the
121+
//| documentation of ``MathOperation`` for the specific functions available.
122+
//|
123+
//| The properties can all be changed at run-time.
124+
//|
125+
//| An Math only updates if it is actually associated with a playing `Synthesizer`,
126+
//| including indirectly via a `Note` or another intermediate Math.
127+
//|
128+
//| Using the same Math as an input to multiple other Maths or Notes is OK, but
129+
//| the result if an Math is tied to multiple Synthtesizer objects is undefined.
130+
//|
131+
//| In the current implementation, Maths are updated every 256 samples. This
132+
//| should be considered an implementation detail.
133+
//| """
134+
//|
135+
//| def __init__(
136+
//| self,
137+
//| operation: MathOperation,
138+
//| a: BlockInput,
139+
//| b: BlockInput = 0.0,
140+
//| c: BlockInput = 1.0,
141+
//| ):
142+
//| pass
143+
static const mp_arg_t math_properties[] = {
144+
{ MP_QSTR_operation, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL } },
145+
{ MP_QSTR_a, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL } },
146+
{ MP_QSTR_b, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
147+
{ MP_QSTR_c, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(1) } },
148+
};
149+
150+
STATIC mp_obj_t synthio_math_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
151+
mp_arg_val_t args[MP_ARRAY_SIZE(math_properties)];
152+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(math_properties), math_properties, args);
153+
154+
synthio_math_obj_t *self = m_new_obj(synthio_math_obj_t);
155+
self->base.base.type = &synthio_math_type;
156+
157+
self->base.last_tick = synthio_global_tick;
158+
159+
mp_obj_t result = MP_OBJ_FROM_PTR(self);
160+
properties_construct_helper(result, math_properties, args, MP_ARRAY_SIZE(math_properties));
161+
162+
return result;
163+
};
164+
165+
//| a: BlockInput
166+
//| """The first input to the operation"""
167+
STATIC mp_obj_t synthio_math_get_a(mp_obj_t self_in) {
168+
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
169+
return common_hal_synthio_math_get_input_obj(self, 0);
170+
}
171+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_a_obj, synthio_math_get_a);
172+
173+
STATIC mp_obj_t synthio_math_set_a(mp_obj_t self_in, mp_obj_t arg) {
174+
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
175+
common_hal_synthio_math_set_input_obj(self, 0, arg, MP_QSTR_a);
176+
return mp_const_none;
177+
}
178+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_math_set_a_obj, synthio_math_set_a);
179+
MP_PROPERTY_GETSET(synthio_math_a_obj,
180+
(mp_obj_t)&synthio_math_get_a_obj,
181+
(mp_obj_t)&synthio_math_set_a_obj);
182+
183+
184+
//| b: BlockInput
185+
//| """The second input to the operation"""
186+
STATIC mp_obj_t synthio_math_get_b(mp_obj_t self_in) {
187+
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
188+
return common_hal_synthio_math_get_input_obj(self, 1);
189+
}
190+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_b_obj, synthio_math_get_b);
191+
192+
STATIC mp_obj_t synthio_math_set_b(mp_obj_t self_in, mp_obj_t arg) {
193+
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
194+
common_hal_synthio_math_set_input_obj(self, 1, arg, MP_QSTR_b);
195+
return mp_const_none;
196+
}
197+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_math_set_b_obj, synthio_math_set_b);
198+
MP_PROPERTY_GETSET(synthio_math_b_obj,
199+
(mp_obj_t)&synthio_math_get_b_obj,
200+
(mp_obj_t)&synthio_math_set_b_obj);
201+
202+
203+
//| c: BlockInput
204+
//| """The third input to the operation"""
205+
STATIC mp_obj_t synthio_math_get_c(mp_obj_t self_in) {
206+
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
207+
return common_hal_synthio_math_get_input_obj(self, 2);
208+
}
209+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_c_obj, synthio_math_get_c);
210+
211+
STATIC mp_obj_t synthio_math_set_c(mp_obj_t self_in, mp_obj_t arg) {
212+
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
213+
common_hal_synthio_math_set_input_obj(self, 2, arg, MP_QSTR_c);
214+
return mp_const_none;
215+
}
216+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_math_set_c_obj, synthio_math_set_c);
217+
MP_PROPERTY_GETSET(synthio_math_c_obj,
218+
(mp_obj_t)&synthio_math_get_c_obj,
219+
(mp_obj_t)&synthio_math_set_c_obj);
220+
221+
222+
//| operation: MathOperation
223+
//| """The function to compute"""
224+
STATIC mp_obj_t synthio_math_get_operation(mp_obj_t self_in) {
225+
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
226+
return cp_enum_find(&synthio_math_operation_type, common_hal_synthio_math_get_operation(self));
227+
}
228+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_operation_obj, synthio_math_get_operation);
229+
230+
STATIC mp_obj_t synthio_math_set_operation(mp_obj_t self_in, mp_obj_t arg) {
231+
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
232+
common_hal_synthio_math_set_operation(self, cp_enum_value(&synthio_math_operation_type, arg, MP_QSTR_operation));
233+
return mp_const_none;
234+
}
235+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_math_set_operation_obj, synthio_math_set_operation);
236+
MP_PROPERTY_GETSET(synthio_math_operation_obj,
237+
(mp_obj_t)&synthio_math_get_operation_obj,
238+
(mp_obj_t)&synthio_math_set_operation_obj);
239+
240+
241+
242+
//|
243+
//| value: float
244+
//| """The value of the oscillator (read-only)"""
245+
//|
246+
STATIC mp_obj_t synthio_math_get_value(mp_obj_t self_in) {
247+
synthio_math_obj_t *self = MP_OBJ_TO_PTR(self_in);
248+
return mp_obj_new_float(common_hal_synthio_math_get_value(self));
249+
}
250+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_math_get_value_obj, synthio_math_get_value);
251+
252+
MP_PROPERTY_GETTER(synthio_math_value_obj,
253+
(mp_obj_t)&synthio_math_get_value_obj);
254+
255+
256+
static void math_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
257+
(void)kind;
258+
properties_print_helper(print, self_in, math_properties, MP_ARRAY_SIZE(math_properties));
259+
}
260+
261+
STATIC const mp_rom_map_elem_t synthio_math_locals_dict_table[] = {
262+
{ MP_ROM_QSTR(MP_QSTR_a), MP_ROM_PTR(&synthio_math_a_obj) },
263+
{ MP_ROM_QSTR(MP_QSTR_b), MP_ROM_PTR(&synthio_math_b_obj) },
264+
{ MP_ROM_QSTR(MP_QSTR_c), MP_ROM_PTR(&synthio_math_c_obj) },
265+
{ MP_ROM_QSTR(MP_QSTR_operation), MP_ROM_PTR(&synthio_math_operation_obj) },
266+
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&synthio_math_value_obj) },
267+
};
268+
STATIC MP_DEFINE_CONST_DICT(synthio_math_locals_dict, synthio_math_locals_dict_table);
269+
270+
271+
STATIC const synthio_block_proto_t math_proto = {
272+
MP_PROTO_IMPLEMENT(MP_QSTR_synthio_block)
273+
.tick = common_hal_synthio_math_tick,
274+
};
275+
276+
const mp_obj_type_t synthio_math_type = {
277+
{ &mp_type_type },
278+
.flags = MP_TYPE_FLAG_EXTENDED,
279+
.name = MP_QSTR_Math,
280+
.make_new = synthio_math_make_new,
281+
.locals_dict = (mp_obj_dict_t *)&synthio_math_locals_dict,
282+
.print = math_print,
283+
MP_TYPE_EXTENDED_FIELDS(
284+
.protocol = &math_proto,
285+
),
286+
};

shared-bindings/synthio/Math.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* This file is part of the Micro Python project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2023 Jeff Epler for Adafruit Industries
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+
#pragma once
28+
29+
#include "py/obj.h"
30+
31+
typedef enum {
32+
OP_SUM,
33+
OP_ADD_SUB,
34+
OP_PRODUCT,
35+
OP_MUL_DIV,
36+
OP_SCALE_OFFSET,
37+
OP_OFFSET_SCALE,
38+
OP_LERP,
39+
OP_CONSTRAINED_LERP,
40+
OP_DIV_ADD,
41+
OP_ADD_DIV,
42+
OP_MID,
43+
OP_MIN,
44+
OP_MAX,
45+
OP_ABS
46+
} synthio_math_operation_t;
47+
48+
typedef struct synthio_math_obj synthio_math_obj_t;
49+
extern const mp_obj_type_t synthio_math_type;
50+
extern const mp_obj_type_t synthio_math_operation_type;
51+
52+
mp_obj_t common_hal_synthio_math_get_input_obj(synthio_math_obj_t *self, size_t i);
53+
void common_hal_synthio_math_set_input_obj(synthio_math_obj_t *self, size_t i, mp_obj_t arg, qstr argname);
54+
55+
synthio_math_operation_t common_hal_synthio_math_get_operation(synthio_math_obj_t *self);
56+
void common_hal_synthio_math_set_operation(synthio_math_obj_t *self, synthio_math_operation_t arg);
57+
58+
mp_float_t common_hal_synthio_math_get_value(synthio_math_obj_t *self);
59+
60+
mp_float_t common_hal_synthio_math_tick(mp_obj_t self_in);

0 commit comments

Comments
 (0)