Skip to content

Commit a7da245

Browse files
committed
synthio: Add synthio.Note
This class allows much more expressive sound synthesis: * tremolo & vibrato * arbitrary frequency * different evelope & waveform per note * all properties dynamically settable from Python code
1 parent bc03e03 commit a7da245

File tree

20 files changed

+863
-62
lines changed

20 files changed

+863
-62
lines changed

ports/unix/variants/coverage/mpconfigvariant.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ SRC_BITMAP := \
4242
shared-bindings/struct/__init__.c \
4343
shared-bindings/synthio/__init__.c \
4444
shared-bindings/synthio/MidiTrack.c \
45+
shared-bindings/synthio/Note.c \
4546
shared-bindings/synthio/Synthesizer.c \
4647
shared-bindings/traceback/__init__.c \
4748
shared-bindings/util.c \
@@ -64,6 +65,7 @@ SRC_BITMAP := \
6465
shared-module/struct/__init__.c \
6566
shared-module/synthio/__init__.c \
6667
shared-module/synthio/MidiTrack.c \
68+
shared-module/synthio/Note.c \
6769
shared-module/synthio/Synthesizer.c \
6870
shared-module/traceback/__init__.c \
6971
shared-module/zlib/__init__.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/MidiTrack.c \
654+
synthio/Note.c \
654655
synthio/Synthesizer.c \
655656
synthio/__init__.c \
656657
terminalio/Terminal.c \

shared-bindings/synthio/Note.c

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
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 <stdint.h>
29+
30+
#include "py/objproperty.h"
31+
#include "py/runtime.h"
32+
#include "shared-bindings/util.h"
33+
#include "shared-bindings/synthio/Note.h"
34+
#include "shared-module/synthio/Note.h"
35+
36+
static const mp_arg_t note_properties[] = {
37+
{ MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL } },
38+
{ MP_QSTR_amplitude, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(1) } },
39+
{ MP_QSTR_tremolo_rate, MP_ARG_OBJ, {.u_obj = NULL } },
40+
{ MP_QSTR_tremolo_depth, MP_ARG_OBJ, {.u_obj = NULL } },
41+
{ MP_QSTR_vibrato_rate, MP_ARG_OBJ, {.u_obj = NULL } },
42+
{ MP_QSTR_vibrato_depth, MP_ARG_OBJ, {.u_obj = NULL } },
43+
{ MP_QSTR_waveform, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE } },
44+
{ MP_QSTR_envelope, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE } },
45+
};
46+
//| class Note:
47+
//| def __init__(
48+
//| self,
49+
//| frequency: float,
50+
//| amplitude: float = 1.0,
51+
//| waveform: Optional[ReadableBuffer] = None,
52+
//| envelope: Optional[Envelope] = None,
53+
//| tremolo_depth: float = 0.0,
54+
//| tremolo_rate: float = 0.0,
55+
//| vibrato_depth: float = 0.0,
56+
//| vibrato_rate: float = 0.0,
57+
//| ) -> None:
58+
//| """Construct a Note object, with a frequency in Hz, and optional amplitude (volume), waveform, envelope, tremolo (volume change) and vibrato (frequency change).
59+
//|
60+
//| If waveform or envelope are `None` the synthesizer object's default waveform or envelope are used.
61+
//|
62+
//| If the same Note object is played on multiple Synthesizer objects, the result is undefined.
63+
//| """
64+
STATIC mp_obj_t synthio_note_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
65+
enum { ARG_frequency, ARG_amplitude, ARG_waveform, ARG_envelope, ARG_tremolo_rate, ARG_tremolo_depth, ARG_vibrato_rate, ARG_vibrato_depth };
66+
mp_arg_val_t args[MP_ARRAY_SIZE(note_properties)];
67+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(note_properties), note_properties, args);
68+
69+
synthio_note_obj_t *self = m_new_obj(synthio_note_obj_t);
70+
self->base.type = &synthio_note_type;
71+
72+
mp_obj_t result = MP_OBJ_FROM_PTR(self);
73+
for (size_t i = 0; i < MP_ARRAY_SIZE(note_properties); i++) {
74+
if (args[i].u_obj != NULL) {
75+
mp_store_attr(result, note_properties[i].qst, args[i].u_obj);
76+
}
77+
}
78+
79+
return result;
80+
};
81+
82+
//| frequency: float
83+
//| """The base frequency of the note, in Hz."""
84+
STATIC mp_obj_t synthio_note_get_frequency(mp_obj_t self_in) {
85+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
86+
return mp_obj_new_float(common_hal_synthio_note_get_frequency(self));
87+
}
88+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_frequency_obj, synthio_note_get_frequency);
89+
90+
STATIC mp_obj_t synthio_note_set_frequency(mp_obj_t self_in, mp_obj_t arg) {
91+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
92+
common_hal_synthio_note_set_frequency(self, mp_obj_get_float(arg));
93+
return mp_const_none;
94+
}
95+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_frequency_obj, synthio_note_set_frequency);
96+
MP_PROPERTY_GETSET(synthio_note_frequency_obj,
97+
(mp_obj_t)&synthio_note_get_frequency_obj,
98+
(mp_obj_t)&synthio_note_set_frequency_obj);
99+
100+
//| amplitude: float
101+
//| """The base amplitude of the note, from 0 to 1"""
102+
STATIC mp_obj_t synthio_note_get_amplitude(mp_obj_t self_in) {
103+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
104+
return mp_obj_new_float(common_hal_synthio_note_get_amplitude(self));
105+
}
106+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_amplitude_obj, synthio_note_get_amplitude);
107+
108+
STATIC mp_obj_t synthio_note_set_amplitude(mp_obj_t self_in, mp_obj_t arg) {
109+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
110+
common_hal_synthio_note_set_amplitude(self, mp_obj_get_float(arg));
111+
return mp_const_none;
112+
}
113+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_amplitude_obj, synthio_note_set_amplitude);
114+
MP_PROPERTY_GETSET(synthio_note_amplitude_obj,
115+
(mp_obj_t)&synthio_note_get_amplitude_obj,
116+
(mp_obj_t)&synthio_note_set_amplitude_obj);
117+
118+
119+
//| tremolo_depth: float
120+
//| """The tremolo depth of the note, from 0 to 1"""
121+
STATIC mp_obj_t synthio_note_get_tremolo_depth(mp_obj_t self_in) {
122+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
123+
return mp_obj_new_float(common_hal_synthio_note_get_tremolo_depth(self));
124+
}
125+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_tremolo_depth_obj, synthio_note_get_tremolo_depth);
126+
127+
STATIC mp_obj_t synthio_note_set_tremolo_depth(mp_obj_t self_in, mp_obj_t arg) {
128+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
129+
common_hal_synthio_note_set_tremolo_depth(self, mp_obj_get_float(arg));
130+
return mp_const_none;
131+
}
132+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_tremolo_depth_obj, synthio_note_set_tremolo_depth);
133+
MP_PROPERTY_GETSET(synthio_note_tremolo_depth_obj,
134+
(mp_obj_t)&synthio_note_get_tremolo_depth_obj,
135+
(mp_obj_t)&synthio_note_set_tremolo_depth_obj);
136+
137+
//| tremolo_rate: float
138+
//| """The tremolo rate of the note, in Hz."""
139+
STATIC mp_obj_t synthio_note_get_tremolo_rate(mp_obj_t self_in) {
140+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
141+
return mp_obj_new_float(common_hal_synthio_note_get_tremolo_rate(self));
142+
}
143+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_tremolo_rate_obj, synthio_note_get_tremolo_rate);
144+
145+
STATIC mp_obj_t synthio_note_set_tremolo_rate(mp_obj_t self_in, mp_obj_t arg) {
146+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
147+
common_hal_synthio_note_set_tremolo_rate(self, mp_obj_get_float(arg));
148+
return mp_const_none;
149+
}
150+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_tremolo_rate_obj, synthio_note_set_tremolo_rate);
151+
MP_PROPERTY_GETSET(synthio_note_tremolo_rate_obj,
152+
(mp_obj_t)&synthio_note_get_tremolo_rate_obj,
153+
(mp_obj_t)&synthio_note_set_tremolo_rate_obj);
154+
155+
//| vibrato_depth: float
156+
//| """The vibrato depth of the note, from 0 to 1"""
157+
STATIC mp_obj_t synthio_note_get_vibrato_depth(mp_obj_t self_in) {
158+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
159+
return mp_obj_new_float(common_hal_synthio_note_get_vibrato_depth(self));
160+
}
161+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_vibrato_depth_obj, synthio_note_get_vibrato_depth);
162+
163+
STATIC mp_obj_t synthio_note_set_vibrato_depth(mp_obj_t self_in, mp_obj_t arg) {
164+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
165+
common_hal_synthio_note_set_vibrato_depth(self, mp_obj_get_float(arg));
166+
return mp_const_none;
167+
}
168+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_vibrato_depth_obj, synthio_note_set_vibrato_depth);
169+
MP_PROPERTY_GETSET(synthio_note_vibrato_depth_obj,
170+
(mp_obj_t)&synthio_note_get_vibrato_depth_obj,
171+
(mp_obj_t)&synthio_note_set_vibrato_depth_obj);
172+
173+
//| vibrato_rate: float
174+
//| """The vibrato rate of the note, in Hz."""
175+
STATIC mp_obj_t synthio_note_get_vibrato_rate(mp_obj_t self_in) {
176+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
177+
return mp_obj_new_float(common_hal_synthio_note_get_vibrato_rate(self));
178+
}
179+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_vibrato_rate_obj, synthio_note_get_vibrato_rate);
180+
181+
STATIC mp_obj_t synthio_note_set_vibrato_rate(mp_obj_t self_in, mp_obj_t arg) {
182+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
183+
common_hal_synthio_note_set_vibrato_rate(self, mp_obj_get_float(arg));
184+
return mp_const_none;
185+
}
186+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_vibrato_rate_obj, synthio_note_set_vibrato_rate);
187+
MP_PROPERTY_GETSET(synthio_note_vibrato_rate_obj,
188+
(mp_obj_t)&synthio_note_get_vibrato_rate_obj,
189+
(mp_obj_t)&synthio_note_set_vibrato_rate_obj);
190+
191+
//| waveform: Optional[ReadableBuffer]
192+
//| """The waveform of this note. Setting the waveform to a buffer of a different size resets the note's phase."""
193+
STATIC mp_obj_t synthio_note_get_waveform(mp_obj_t self_in) {
194+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
195+
return common_hal_synthio_note_get_waveform_obj(self);
196+
}
197+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_waveform_obj, synthio_note_get_waveform);
198+
199+
STATIC mp_obj_t synthio_note_set_waveform(mp_obj_t self_in, mp_obj_t arg) {
200+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
201+
common_hal_synthio_note_set_waveform(self, arg);
202+
return mp_const_none;
203+
}
204+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_waveform_obj, synthio_note_set_waveform);
205+
MP_PROPERTY_GETSET(synthio_note_waveform_obj,
206+
(mp_obj_t)&synthio_note_get_waveform_obj,
207+
(mp_obj_t)&synthio_note_set_waveform_obj);
208+
209+
210+
//| envelope: Envelope
211+
//| """The envelope of this note"""
212+
//|
213+
STATIC mp_obj_t synthio_note_get_envelope(mp_obj_t self_in) {
214+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
215+
return common_hal_synthio_note_get_envelope_obj(self);
216+
}
217+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_envelope_obj, synthio_note_get_envelope);
218+
219+
STATIC mp_obj_t synthio_note_set_envelope(mp_obj_t self_in, mp_obj_t arg) {
220+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
221+
common_hal_synthio_note_set_envelope(self, arg);
222+
return mp_const_none;
223+
}
224+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_envelope_obj, synthio_note_set_envelope);
225+
MP_PROPERTY_GETSET(synthio_note_envelope_obj,
226+
(mp_obj_t)&synthio_note_get_envelope_obj,
227+
(mp_obj_t)&synthio_note_set_envelope_obj);
228+
229+
static void note_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
230+
(void)kind;
231+
properties_print_helper(print, self_in, note_properties, MP_ARRAY_SIZE(note_properties));
232+
}
233+
234+
STATIC const mp_rom_map_elem_t synthio_note_locals_dict_table[] = {
235+
{ MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&synthio_note_frequency_obj) },
236+
{ MP_ROM_QSTR(MP_QSTR_amplitude), MP_ROM_PTR(&synthio_note_amplitude_obj) },
237+
{ MP_ROM_QSTR(MP_QSTR_waveform), MP_ROM_PTR(&synthio_note_waveform_obj) },
238+
{ MP_ROM_QSTR(MP_QSTR_envelope), MP_ROM_PTR(&synthio_note_envelope_obj) },
239+
{ MP_ROM_QSTR(MP_QSTR_tremolo_depth), MP_ROM_PTR(&synthio_note_tremolo_depth_obj) },
240+
{ MP_ROM_QSTR(MP_QSTR_tremolo_rate), MP_ROM_PTR(&synthio_note_tremolo_rate_obj) },
241+
{ MP_ROM_QSTR(MP_QSTR_vibrato_depth), MP_ROM_PTR(&synthio_note_vibrato_depth_obj) },
242+
{ MP_ROM_QSTR(MP_QSTR_vibrato_rate), MP_ROM_PTR(&synthio_note_vibrato_rate_obj) },
243+
};
244+
STATIC MP_DEFINE_CONST_DICT(synthio_note_locals_dict, synthio_note_locals_dict_table);
245+
246+
const mp_obj_type_t synthio_note_type = {
247+
{ &mp_type_type },
248+
.name = MP_QSTR_Note,
249+
.make_new = synthio_note_make_new,
250+
.locals_dict = (mp_obj_dict_t *)&synthio_note_locals_dict,
251+
.print = note_print,
252+
};

shared-bindings/synthio/Note.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#pragma once
2+
3+
#include "py/obj.h"
4+
5+
typedef struct synthio_note_obj synthio_note_obj_t;
6+
extern const mp_obj_type_t synthio_note_type;
7+
8+
mp_float_t common_hal_synthio_note_get_frequency(synthio_note_obj_t *self);
9+
void common_hal_synthio_note_set_frequency(synthio_note_obj_t *self, mp_float_t value);
10+
11+
mp_float_t common_hal_synthio_note_get_amplitude(synthio_note_obj_t *self);
12+
void common_hal_synthio_note_set_amplitude(synthio_note_obj_t *self, mp_float_t value);
13+
14+
mp_float_t common_hal_synthio_note_get_tremolo_rate(synthio_note_obj_t *self);
15+
void common_hal_synthio_note_set_tremolo_rate(synthio_note_obj_t *self, mp_float_t value);
16+
17+
mp_float_t common_hal_synthio_note_get_tremolo_depth(synthio_note_obj_t *self);
18+
void common_hal_synthio_note_set_tremolo_depth(synthio_note_obj_t *self, mp_float_t value);
19+
20+
mp_float_t common_hal_synthio_note_get_vibrato_rate(synthio_note_obj_t *self);
21+
void common_hal_synthio_note_set_vibrato_rate(synthio_note_obj_t *self, mp_float_t value);
22+
23+
mp_float_t common_hal_synthio_note_get_vibrato_depth(synthio_note_obj_t *self);
24+
void common_hal_synthio_note_set_vibrato_depth(synthio_note_obj_t *self, mp_float_t value);
25+
26+
mp_obj_t common_hal_synthio_note_get_waveform_obj(synthio_note_obj_t *self);
27+
void common_hal_synthio_note_set_waveform(synthio_note_obj_t *self, mp_obj_t value);
28+
29+
mp_obj_t common_hal_synthio_note_get_envelope_obj(synthio_note_obj_t *self);
30+
void common_hal_synthio_note_set_envelope(synthio_note_obj_t *self, mp_obj_t value);

0 commit comments

Comments
 (0)