forked from adafruit/circuitpython
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSynthesizer.c
442 lines (383 loc) · 19.6 KB
/
Synthesizer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2021 Artyom Skrobov
// SPDX-FileCopyrightText: Copyright (c) 2023 Jeff Epler for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <stdint.h>
#include "shared/runtime/context_manager_helpers.h"
#include "py/binary.h"
#include "py/objproperty.h"
#include "py/runtime.h"
#include "py/enum.h"
#include "shared-bindings/util.h"
#include "shared-bindings/synthio/Biquad.h"
#include "shared-bindings/synthio/Synthesizer.h"
#include "shared-bindings/synthio/LFO.h"
#include "shared-bindings/synthio/__init__.h"
#include "shared-bindings/audiocore/__init__.h"
//| NoteSequence = Sequence[Union[int, Note]]
//| """A sequence of notes, which can each be integer MIDI note numbers or `Note` objects"""
//| NoteOrNoteSequence = Union[int, Note, NoteSequence]
//| """A note or sequence of notes"""
//| LFOOrLFOSequence = Union["LFO", Sequence["LFO"]]
//| """An LFO or a sequence of LFOs"""
//|
//|
//| class Synthesizer:
//| def __init__(
//| self,
//| *,
//| sample_rate: int = 11025,
//| channel_count: int = 1,
//| waveform: Optional[ReadableBuffer] = None,
//| envelope: Optional[Envelope] = None,
//| ) -> None:
//| """Create a synthesizer object.
//|
//| This API is experimental.
//|
//| Integer notes use MIDI note numbering, with 60 being C4 or Middle C,
//| approximately 262Hz. Integer notes use the given waveform & envelope,
//| and do not support advanced features like tremolo or vibrato.
//|
//| :param int sample_rate: The desired playback sample rate; higher sample rate requires more memory
//| :param int channel_count: The number of output channels (1=mono, 2=stereo)
//| :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)
//| :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.
//| """
//|
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) {
enum { ARG_sample_rate, ARG_channel_count, ARG_waveform, ARG_envelope };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} },
{ MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} },
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
{ MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
synthio_synthesizer_obj_t *self = mp_obj_malloc(synthio_synthesizer_obj_t, &synthio_synthesizer_type);
common_hal_synthio_synthesizer_construct(self,
args[ARG_sample_rate].u_int,
args[ARG_channel_count].u_int,
args[ARG_waveform].u_obj,
args[ARG_envelope].u_obj);
return MP_OBJ_FROM_PTR(self);
}
static void check_for_deinit(synthio_synthesizer_obj_t *self) {
audiosample_check_for_deinit(&self->synth.base);
}
//| def press(self, /, press: NoteOrNoteSequence = ()) -> None:
//| """Turn some notes on.
//|
//| Pressing a note that was already pressed has no effect.
//|
//| :param NoteOrNoteSequence press: Any sequence of notes."""
//|
static mp_obj_t synthio_synthesizer_press(mp_obj_t self_in, mp_obj_t press) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
common_hal_synthio_synthesizer_press(self, press);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_press_obj, synthio_synthesizer_press);
//| def release(self, /, release: NoteOrNoteSequence = ()) -> None:
//| """Turn some notes off.
//|
//| Releasing a note that was already released has no effect.
//|
//| :param NoteOrNoteSequence release: Any sequence of notes."""
//|
static mp_obj_t synthio_synthesizer_release(mp_obj_t self_in, mp_obj_t release) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
common_hal_synthio_synthesizer_release(self, release);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_release_obj, synthio_synthesizer_release);
//| def change(
//| self,
//| release: NoteOrNoteSequence = (),
//| press: NoteOrNoteSequence = (),
//| retrigger: LFOOrLFOSequence = (),
//| ) -> None:
//| """Start notes, stop them, and/or re-trigger some LFOs.
//|
//| The changes all happen atomically with respect to output generation.
//|
//| It is OK to release note that was not actually turned on.
//|
//| Pressing a note that was already pressed returns it to the attack phase
//| but without resetting its amplitude. Releasing a note and immediately
//| pressing it again returns it to the attack phase with an initial
//| amplitude of 0.
//|
//| At the same time, the passed LFOs (if any) are retriggered.
//|
//| :param NoteOrNoteSequence release: Any sequence of notes.
//| :param NoteOrNoteSequence press: Any sequence of notes.
//| :param LFOOrLFOSequence retrigger: Any sequence of LFOs.
//|
//| Note: for compatibility, ``release_then_press`` may be used as an alias
//| for this function. This compatibility name will be removed in 9.0."""
//|
static mp_obj_t synthio_synthesizer_change(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_release, ARG_press, ARG_retrigger };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_release, MP_ARG_OBJ, {.u_obj = mp_const_empty_tuple } },
{ MP_QSTR_press, MP_ARG_OBJ, {.u_obj = mp_const_empty_tuple } },
{ MP_QSTR_retrigger, MP_ARG_OBJ, {.u_obj = mp_const_empty_tuple } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
check_for_deinit(self);
common_hal_synthio_synthesizer_release(self, args[ARG_release].u_obj);
common_hal_synthio_synthesizer_press(self, args[ARG_press].u_obj);
common_hal_synthio_synthesizer_retrigger(self, args[ARG_retrigger].u_obj);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_change_obj, 1, synthio_synthesizer_change);
//
//| def release_all_then_press(self, /, press: NoteOrNoteSequence) -> None:
//| """Turn any currently-playing notes off, then turn on the given notes
//|
//| Releasing a note and immediately pressing it again returns it to the
//| attack phase with an initial amplitude of 0.
//|
//| :param NoteOrNoteSequence press: Any sequence of notes."""
//|
static mp_obj_t synthio_synthesizer_release_all_then_press(mp_obj_t self_in, mp_obj_t press) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
common_hal_synthio_synthesizer_release_all(self);
common_hal_synthio_synthesizer_press(self, press);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_release_all_then_press_obj, synthio_synthesizer_release_all_then_press);
//
//| def release_all(self) -> None:
//| """Turn any currently-playing notes off"""
//|
static mp_obj_t synthio_synthesizer_release_all(mp_obj_t self_in) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
common_hal_synthio_synthesizer_release_all(self);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_release_all_obj, synthio_synthesizer_release_all);
//| def deinit(self) -> None:
//| """Deinitialises the object and releases any memory resources for reuse."""
//| ...
//|
static mp_obj_t synthio_synthesizer_deinit(mp_obj_t self_in) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_synthio_synthesizer_deinit(self);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_deinit_obj, synthio_synthesizer_deinit);
//| def __enter__(self) -> Synthesizer:
//| """No-op used by Context Managers."""
//| ...
//|
// Provided by context manager helper.
//|
//| def __exit__(self) -> None:
//| """Automatically deinitializes the hardware when exiting a context. See
//| :ref:`lifetime-and-contextmanagers` for more info."""
//| ...
//|
static mp_obj_t synthio_synthesizer_obj___exit__(size_t n_args, const mp_obj_t *args) {
(void)n_args;
common_hal_synthio_synthesizer_deinit(args[0]);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(synthio_synthesizer___exit___obj, 4, 4, synthio_synthesizer_obj___exit__);
//| envelope: Optional[Envelope]
//| """The envelope to apply to all notes. `None`, the default envelope, instantly turns notes on and off. The envelope may be changed dynamically, but it affects all notes (even currently playing notes)"""
static mp_obj_t synthio_synthesizer_obj_get_envelope(mp_obj_t self_in) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
return synthio_synth_envelope_get(&self->synth);
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_get_envelope_obj, synthio_synthesizer_obj_get_envelope);
static mp_obj_t synthio_synthesizer_obj_set_envelope(mp_obj_t self_in, mp_obj_t envelope) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
synthio_synth_envelope_set(&self->synth, envelope);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_set_envelope_obj, synthio_synthesizer_obj_set_envelope);
MP_PROPERTY_GETSET(synthio_synthesizer_envelope_obj,
(mp_obj_t)&synthio_synthesizer_get_envelope_obj,
(mp_obj_t)&synthio_synthesizer_set_envelope_obj);
//| sample_rate: int
//| """32 bit value that tells how quickly samples are played in Hertz (cycles per second)."""
//| pressed: NoteSequence
//| """A sequence of the currently pressed notes (read-only property).
//|
//| This does not include notes in the release phase of the envelope."""
//|
static mp_obj_t synthio_synthesizer_obj_get_pressed(mp_obj_t self_in) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
return common_hal_synthio_synthesizer_get_pressed_notes(self);
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_get_pressed_obj, synthio_synthesizer_obj_get_pressed);
MP_PROPERTY_GETTER(synthio_synthesizer_pressed_obj,
(mp_obj_t)&synthio_synthesizer_get_pressed_obj);
//| def note_info(self, note: Note) -> Tuple[Optional[EnvelopeState], float]:
//| """Get info about a note's current envelope state
//|
//| If the note is currently playing (including in the release phase), the returned value gives the current envelope state and the current envelope value.
//|
//| If the note is not playing on this synthesizer, returns the tuple ``(None, 0.0)``."""
//|
static mp_obj_t synthio_synthesizer_obj_note_info(mp_obj_t self_in, mp_obj_t note) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
mp_float_t vol = MICROPY_FLOAT_CONST(0.0);
envelope_state_e state = common_hal_synthio_synthesizer_note_info(self, note, &vol);
return MP_OBJ_NEW_TUPLE(
cp_enum_find(&synthio_note_state_type, state),
mp_obj_new_float(vol));
}
MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_note_info_obj, synthio_synthesizer_obj_note_info);
//| blocks: List[BlockInput]
//| """A list of blocks to advance whether or not they are associated with a playing note.
//|
//| This can be used to implement 'free-running' LFOs. LFOs associated with playing notes are advanced whether or not they are in this list.
//|
//| This property is read-only but its contents may be modified by e.g., calling ``synth.blocks.append()`` or ``synth.blocks.remove()``. It is initially an empty list."""
//|
static mp_obj_t synthio_synthesizer_obj_get_blocks(mp_obj_t self_in) {
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
return common_hal_synthio_synthesizer_get_blocks(self);
}
MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_get_blocks_obj, synthio_synthesizer_obj_get_blocks);
MP_PROPERTY_GETTER(synthio_synthesizer_blocks_obj,
(mp_obj_t)&synthio_synthesizer_get_blocks_obj);
//| max_polyphony: int
//| """Maximum polyphony of the synthesizer (read-only class property)"""
//|
//| def low_pass_filter(cls, frequency: float, Q: float = 0.7071067811865475) -> Biquad:
//| """Construct a low-pass filter with the given parameters.
//|
//| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz
//| of the filter.
//|
//| ``Q`` controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
//|
//| .. note:: This is deprecated in ``9.x.x`` and will be removed in ``10.0.0``. Use `BlockBiquad` objects instead.
//| """
//|
enum passfilter_arg_e { ARG_f0, ARG_Q };
// M_PI is not part of the math.h standard and may not be defined
// And by defining our own we can ensure it uses the correct const format.
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
static const mp_arg_t passfilter_properties[] = {
{ MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
{ MP_QSTR_Q, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL } },
};
static mp_obj_t synthio_synthesizer_lpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) {
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
mp_obj_t self_in = pos_args[0];
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_arg_parse_all(n_pos - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
mp_float_t Q =
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
mp_float_t w0 = f0 / self->synth.base.sample_rate * 2 * MP_PI;
return common_hal_synthio_new_lpf(w0, Q);
}
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_lpf_fun_obj, 1, synthio_synthesizer_lpf);
//| def high_pass_filter(cls, frequency: float, Q: float = 0.7071067811865475) -> Biquad:
//| """Construct a high-pass filter with the given parameters.
//|
//| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz
//| of the filter.
//|
//| ``Q`` controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
//|
//| .. note:: This is deprecated in ``9.x.x`` and will be removed in ``10.0.0``. Use `BlockBiquad` objects instead.
//| """
//|
static mp_obj_t synthio_synthesizer_hpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) {
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
mp_obj_t self_in = pos_args[0];
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_arg_parse_all(n_pos - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
mp_float_t Q =
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
mp_float_t w0 = f0 / self->synth.base.sample_rate * 2 * MP_PI;
return common_hal_synthio_new_hpf(w0, Q);
}
//| def band_pass_filter(cls, frequency: float, Q: float = 0.7071067811865475) -> Biquad:
//| """Construct a band-pass filter with the given parameters.
//|
//| ``frequency``, called f0 in the cookbook, is the center frequency in Hz
//| of the filter.
//|
//| ``Q`` Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
//|
//| The coefficients are scaled such that the filter has a 0dB peak gain.
//|
//| .. note:: This is deprecated in ``9.x.x`` and will be removed in ``10.0.0``. Use `BlockBiquad` objects instead.
//| """
//|
//|
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_hpf_fun_obj, 1, synthio_synthesizer_hpf);
static mp_obj_t synthio_synthesizer_bpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) {
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
mp_obj_t self_in = pos_args[0];
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_arg_parse_all(n_pos - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
mp_float_t Q =
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
mp_float_t w0 = f0 / self->synth.base.sample_rate * 2 * MP_PI;
return common_hal_synthio_new_bpf(w0, Q);
}
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_bpf_fun_obj, 1, synthio_synthesizer_bpf);
static const mp_rom_map_elem_t synthio_synthesizer_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_press), MP_ROM_PTR(&synthio_synthesizer_press_obj) },
{ MP_ROM_QSTR(MP_QSTR_release), MP_ROM_PTR(&synthio_synthesizer_release_obj) },
{ MP_ROM_QSTR(MP_QSTR_release_all), MP_ROM_PTR(&synthio_synthesizer_release_all_obj) },
{ MP_ROM_QSTR(MP_QSTR_change), MP_ROM_PTR(&synthio_synthesizer_change_obj) },
{ MP_ROM_QSTR(MP_QSTR_release_then_press), MP_ROM_PTR(&synthio_synthesizer_change_obj) },
{ MP_ROM_QSTR(MP_QSTR_release_all_then_press), MP_ROM_PTR(&synthio_synthesizer_release_all_then_press_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&synthio_synthesizer_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&synthio_synthesizer___exit___obj) },
{ MP_ROM_QSTR(MP_QSTR_low_pass_filter), MP_ROM_PTR(&synthio_synthesizer_lpf_fun_obj) },
{ MP_ROM_QSTR(MP_QSTR_high_pass_filter), MP_ROM_PTR(&synthio_synthesizer_hpf_fun_obj) },
{ MP_ROM_QSTR(MP_QSTR_band_pass_filter), MP_ROM_PTR(&synthio_synthesizer_bpf_fun_obj) },
// Properties
{ MP_ROM_QSTR(MP_QSTR_envelope), MP_ROM_PTR(&synthio_synthesizer_envelope_obj) },
{ MP_ROM_QSTR(MP_QSTR_max_polyphony), MP_ROM_INT(CIRCUITPY_SYNTHIO_MAX_CHANNELS) },
{ MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&synthio_synthesizer_pressed_obj) },
{ MP_ROM_QSTR(MP_QSTR_note_info), MP_ROM_PTR(&synthio_synthesizer_note_info_obj) },
{ MP_ROM_QSTR(MP_QSTR_blocks), MP_ROM_PTR(&synthio_synthesizer_blocks_obj) },
AUDIOSAMPLE_FIELDS,
};
static MP_DEFINE_CONST_DICT(synthio_synthesizer_locals_dict, synthio_synthesizer_locals_dict_table);
static const audiosample_p_t synthio_synthesizer_proto = {
MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample)
.reset_buffer = (audiosample_reset_buffer_fun)synthio_synthesizer_reset_buffer,
.get_buffer = (audiosample_get_buffer_fun)synthio_synthesizer_get_buffer,
};
MP_DEFINE_CONST_OBJ_TYPE(
synthio_synthesizer_type,
MP_QSTR_Synthesizer,
MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS,
make_new, synthio_synthesizer_make_new,
locals_dict, &synthio_synthesizer_locals_dict,
protocol, &synthio_synthesizer_proto
);