Skip to content

Commit 854658d

Browse files
committed
codal_port/microbit_soundeffect: Add audio.SoundEffect class.
As per issue #103 and PR #106. Current limitations: - can't play an iterable of SoundEffect objects - if a sound effect is playing with wait=False and another one is started then the first one will be stopped immediately Signed-off-by: Damien George <[email protected]>
1 parent 2b57ddd commit 854658d

File tree

6 files changed

+364
-4
lines changed

6 files changed

+364
-4
lines changed

src/codal_app/microbithal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ void microbit_hal_audio_select_pin(int pin);
160160
void microbit_hal_audio_select_speaker(bool enable);
161161
void microbit_hal_audio_set_volume(int value);
162162
bool microbit_hal_audio_is_expression_active(void);
163-
void microbit_hal_audio_play_expression_by_name(const char *name);
163+
void microbit_hal_audio_play_expression(const char *expr);
164164
void microbit_hal_audio_stop_expression(void);
165165

166166
void microbit_hal_audio_init(uint32_t sample_rate);

src/codal_app/microbithal_audio.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,15 @@ bool microbit_hal_audio_is_expression_active(void) {
9393
return sound_synth_active_count > 0;
9494
}
9595

96-
void microbit_hal_audio_play_expression_by_name(const char *name) {
96+
void microbit_hal_audio_play_expression(const char *expr) {
9797
++sound_synth_active_count;
9898
uBit.audio.soundExpressions.stop();
99-
uBit.audio.soundExpressions.playAsync(name);
99+
100+
// `expr` can be a built-in expression name, or expression data.
101+
// If it's expression data this method parses the data and stores
102+
// it in another buffer ready to play. So `expr` does not need
103+
// to live for the duration of the playing.
104+
uBit.audio.soundExpressions.playAsync(expr);
100105
}
101106

102107
void microbit_hal_audio_stop_expression(void) {

src/codal_port/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ SRC_C += \
7272
microbit_pinaudio.c \
7373
microbit_pinmode.c \
7474
microbit_sound.c \
75+
microbit_soundeffect.c \
7576
microbit_soundevent.c \
7677
microbit_speaker.c \
7778
microbit_spi.c \

src/codal_port/microbit_soundeffect.c

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2022 Damien P. George
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 "py/runtime.h"
28+
#include "modmicrobit.h"
29+
30+
#define SOUND_EXPR_TOTAL_LENGTH (72)
31+
32+
#define SOUND_EXPR_WAVE_OFFSET (0)
33+
#define SOUND_EXPR_WAVE_LENGTH (1)
34+
#define SOUND_EXPR_VOLUME_START_OFFSET (1)
35+
#define SOUND_EXPR_VOLUME_START_LENGTH (4)
36+
#define SOUND_EXPR_FREQUENCY_START_OFFSET (5)
37+
#define SOUND_EXPR_FREQUENCY_START_LENGTH (4)
38+
#define SOUND_EXPR_DURATION_OFFSET (9)
39+
#define SOUND_EXPR_DURATION_LENGTH (4)
40+
#define SOUND_EXPR_SHAPE_OFFSET (13)
41+
#define SOUND_EXPR_SHAPE_LENGTH (2)
42+
#define SOUND_EXPR_FREQUENCY_END_OFFSET (18)
43+
#define SOUND_EXPR_FREQUENCY_END_LENGTH (4)
44+
#define SOUND_EXPR_VOLUME_END_OFFSET (26)
45+
#define SOUND_EXPR_VOLUME_END_LENGTH (4)
46+
#define SOUND_EXPR_STEPS_OFFSET (30)
47+
#define SOUND_EXPR_STEPS_LENGTH (4)
48+
#define SOUND_EXPR_FX_CHOICE_OFFSET (34)
49+
#define SOUND_EXPR_FX_CHOICE_LENGTH (2)
50+
#define SOUND_EXPR_FX_PARAM_OFFSET (36)
51+
#define SOUND_EXPR_FX_PARAM_LENGTH (4)
52+
#define SOUND_EXPR_FX_STEPS_OFFSET (40)
53+
#define SOUND_EXPR_FX_STEPS_LENGTH (4)
54+
55+
#define SOUND_EXPR_ENCODE_VOLUME(v) (((v) * 1023 + 127) / 255)
56+
#define SOUND_EXPR_DECODE_VOLUME(v) (((v) * 255 + 511) / 1023)
57+
58+
#define SOUND_EFFECT_WAVE_SINE (0)
59+
#define SOUND_EFFECT_WAVE_SAWTOOTH (1)
60+
#define SOUND_EFFECT_WAVE_TRIANGLE (2)
61+
#define SOUND_EFFECT_WAVE_SQUARE (3)
62+
#define SOUND_EFFECT_WAVE_NOISE (4)
63+
64+
#define SOUND_EFFECT_SHAPE_LINEAR (1)
65+
#define SOUND_EFFECT_SHAPE_CURVE (2)
66+
#define SOUND_EFFECT_SHAPE_LOG (18)
67+
68+
#define SOUND_EFFECT_FX_NONE (0)
69+
#define SOUND_EFFECT_FX_TREMOLO (2)
70+
#define SOUND_EFFECT_FX_VIBRATO (1)
71+
#define SOUND_EFFECT_FX_WARBLE (3)
72+
73+
#define SOUND_EFFECT_DEFAULT_FREQ_START (500)
74+
#define SOUND_EFFECT_DEFAULT_FREQ_END (2500)
75+
#define SOUND_EFFECT_DEFAULT_DURATION (500)
76+
#define SOUND_EFFECT_DEFAULT_VOL_START (255)
77+
#define SOUND_EFFECT_DEFAULT_VOL_END (0)
78+
#define SOUND_EFFECT_DEFAULT_WAVE (SOUND_EFFECT_WAVE_SQUARE)
79+
#define SOUND_EFFECT_DEFAULT_FX (SOUND_EFFECT_FX_NONE)
80+
#define SOUND_EFFECT_DEFAULT_SHAPE (SOUND_EFFECT_SHAPE_LOG)
81+
82+
typedef struct _microbit_soundeffect_obj_t {
83+
mp_obj_base_t base;
84+
bool is_mutable;
85+
char sound_expr[SOUND_EXPR_TOTAL_LENGTH];
86+
} microbit_soundeffect_obj_t;
87+
88+
typedef struct _soundeffect_attr_t {
89+
uint16_t qst;
90+
uint8_t offset;
91+
uint8_t length;
92+
} soundeffect_attr_t;
93+
94+
STATIC const uint16_t wave_to_qstr_table[5] = {
95+
[SOUND_EFFECT_WAVE_SINE] = MP_QSTR_WAVE_SINE,
96+
[SOUND_EFFECT_WAVE_SAWTOOTH] = MP_QSTR_WAVE_SAWTOOTH,
97+
[SOUND_EFFECT_WAVE_TRIANGLE] = MP_QSTR_WAVE_TRIANGLE,
98+
[SOUND_EFFECT_WAVE_SQUARE] = MP_QSTR_WAVE_SQUARE,
99+
[SOUND_EFFECT_WAVE_NOISE] = MP_QSTR_WAVE_NOISE,
100+
};
101+
102+
STATIC const uint16_t fx_to_qstr_table[4] = {
103+
[SOUND_EFFECT_FX_NONE] = MP_QSTR_FX_NONE,
104+
[SOUND_EFFECT_FX_TREMOLO] = MP_QSTR_FX_TREMOLO,
105+
[SOUND_EFFECT_FX_VIBRATO] = MP_QSTR_FX_VIBRATO,
106+
[SOUND_EFFECT_FX_WARBLE] = MP_QSTR_FX_WARBLE,
107+
};
108+
109+
STATIC const soundeffect_attr_t soundeffect_attr_table[] = {
110+
{ MP_QSTR_freq_start, SOUND_EXPR_FREQUENCY_START_OFFSET, SOUND_EXPR_FREQUENCY_START_LENGTH },
111+
{ MP_QSTR_freq_end, SOUND_EXPR_FREQUENCY_END_OFFSET, SOUND_EXPR_FREQUENCY_END_LENGTH },
112+
{ MP_QSTR_duration, SOUND_EXPR_DURATION_OFFSET, SOUND_EXPR_DURATION_LENGTH },
113+
{ MP_QSTR_vol_start, SOUND_EXPR_VOLUME_START_OFFSET, SOUND_EXPR_VOLUME_START_LENGTH },
114+
{ MP_QSTR_vol_end, SOUND_EXPR_VOLUME_END_OFFSET, SOUND_EXPR_VOLUME_END_LENGTH },
115+
{ MP_QSTR_wave, SOUND_EXPR_WAVE_OFFSET, SOUND_EXPR_WAVE_LENGTH },
116+
{ MP_QSTR_fx, SOUND_EXPR_FX_CHOICE_OFFSET, SOUND_EXPR_FX_CHOICE_LENGTH },
117+
{ MP_QSTR_shape, SOUND_EXPR_SHAPE_OFFSET, SOUND_EXPR_SHAPE_LENGTH },
118+
};
119+
120+
const char *microbit_soundeffect_get_sound_expr_data(mp_obj_t self_in) {
121+
const microbit_soundeffect_obj_t *self = MP_OBJ_TO_PTR(self_in);
122+
return &self->sound_expr[0];
123+
}
124+
125+
STATIC void sound_expr_encode(microbit_soundeffect_obj_t *self, size_t offset, size_t length, unsigned int value) {
126+
if (offset == SOUND_EXPR_VOLUME_START_OFFSET || offset == SOUND_EXPR_VOLUME_END_OFFSET) {
127+
value = SOUND_EXPR_ENCODE_VOLUME(value);
128+
}
129+
for (size_t i = length; i > 0; --i) {
130+
self->sound_expr[offset + i - 1] = '0' + value % 10;
131+
value /= 10;
132+
}
133+
}
134+
135+
STATIC unsigned int sound_expr_decode(const microbit_soundeffect_obj_t *self, size_t offset, size_t length) {
136+
unsigned int value = 0;
137+
for (size_t i = 0; i < length; ++i) {
138+
value = value * 10 + self->sound_expr[offset + i] - '0';
139+
}
140+
if (offset == SOUND_EXPR_VOLUME_START_OFFSET || offset == SOUND_EXPR_VOLUME_END_OFFSET) {
141+
value = SOUND_EXPR_DECODE_VOLUME(value);
142+
}
143+
return value;
144+
}
145+
146+
STATIC void microbit_soundeffect_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
147+
const microbit_soundeffect_obj_t *self = MP_OBJ_TO_PTR(self_in);
148+
149+
unsigned int freq_start = sound_expr_decode(self, SOUND_EXPR_FREQUENCY_START_OFFSET, SOUND_EXPR_FREQUENCY_START_LENGTH);
150+
unsigned int freq_end = sound_expr_decode(self, SOUND_EXPR_FREQUENCY_END_OFFSET, SOUND_EXPR_FREQUENCY_END_LENGTH);
151+
unsigned int duration = sound_expr_decode(self, SOUND_EXPR_DURATION_OFFSET, SOUND_EXPR_DURATION_LENGTH);
152+
unsigned int vol_start = sound_expr_decode(self, SOUND_EXPR_VOLUME_START_OFFSET, SOUND_EXPR_VOLUME_START_LENGTH);
153+
unsigned int vol_end = sound_expr_decode(self, SOUND_EXPR_VOLUME_END_OFFSET, SOUND_EXPR_VOLUME_END_LENGTH);
154+
unsigned int wave = sound_expr_decode(self, SOUND_EXPR_WAVE_OFFSET, SOUND_EXPR_WAVE_LENGTH);
155+
unsigned int fx = sound_expr_decode(self, SOUND_EXPR_FX_CHOICE_OFFSET, SOUND_EXPR_FX_CHOICE_LENGTH);
156+
unsigned int shape = sound_expr_decode(self, SOUND_EXPR_SHAPE_OFFSET, SOUND_EXPR_SHAPE_LENGTH);
157+
158+
if (kind == PRINT_STR) {
159+
mp_printf(print, "SoundEffect("
160+
"freq_start=%d, "
161+
"freq_end=%d, "
162+
"duration=%d, "
163+
"vol_start=%d, "
164+
"vol_end=%d, "
165+
"wave=%q, "
166+
"fx=%q, ",
167+
freq_start,
168+
freq_end,
169+
duration,
170+
vol_start,
171+
vol_end,
172+
wave_to_qstr_table[wave],
173+
fx_to_qstr_table[fx]
174+
);
175+
176+
// Support shape values that don't have a corresponding constant assigned.
177+
switch (shape) {
178+
case SOUND_EFFECT_SHAPE_LINEAR:
179+
mp_printf(print, "shape=SHAPE_LINEAR)");
180+
break;
181+
case SOUND_EFFECT_SHAPE_CURVE:
182+
mp_printf(print, "shape=SHAPE_CURVE)");
183+
break;
184+
case SOUND_EFFECT_SHAPE_LOG:
185+
mp_printf(print, "shape=SHAPE_LOG)");
186+
break;
187+
default:
188+
mp_printf(print, "shape=%d)", shape);
189+
break;
190+
}
191+
} else {
192+
// PRINT_REPR
193+
mp_printf(print, "SoundEffect(%d, %d, %d, %d, %d, %d, %d, %d)",
194+
freq_start, freq_end, duration, vol_start, vol_end, wave, fx, shape);
195+
}
196+
}
197+
198+
// Constructor:
199+
// SoundEffect(freq_start, freq_end, duration, vol_start, vol_end, wave, fx, shape)
200+
STATIC mp_obj_t microbit_soundeffect_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) {
201+
enum { ARG_freq_start, ARG_freq_end, ARG_duration, ARG_vol_start, ARG_vol_end, ARG_wave, ARG_fx, ARG_shape };
202+
static const mp_arg_t allowed_args[] = {
203+
{ MP_QSTR_freq_start, MP_ARG_INT, {.u_int = SOUND_EFFECT_DEFAULT_FREQ_START} },
204+
{ MP_QSTR_freq_end, MP_ARG_INT, {.u_int = SOUND_EFFECT_DEFAULT_FREQ_END} },
205+
{ MP_QSTR_duration, MP_ARG_INT, {.u_int = SOUND_EFFECT_DEFAULT_DURATION} },
206+
{ MP_QSTR_vol_start, MP_ARG_INT, {.u_int = SOUND_EFFECT_DEFAULT_VOL_START} },
207+
{ MP_QSTR_vol_end, MP_ARG_INT, {.u_int = SOUND_EFFECT_DEFAULT_VOL_END} },
208+
{ MP_QSTR_wave, MP_ARG_INT, {.u_int = SOUND_EFFECT_DEFAULT_WAVE} },
209+
{ MP_QSTR_fx, MP_ARG_INT, {.u_int = SOUND_EFFECT_DEFAULT_FX} },
210+
{ MP_QSTR_shape, MP_ARG_INT, {.u_int = SOUND_EFFECT_DEFAULT_SHAPE} },
211+
};
212+
213+
// Parse arguments.
214+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
215+
mp_arg_parse_all_kw_array(n_args, n_kw, args_in, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
216+
217+
// Create sound effect object.
218+
microbit_soundeffect_obj_t *self = m_new_obj(microbit_soundeffect_obj_t);
219+
self->base.type = type;
220+
self->is_mutable = true;
221+
222+
// Initialise base parameters of the sound expression data.
223+
memset(&self->sound_expr[0], '0', SOUND_EXPR_TOTAL_LENGTH);
224+
sound_expr_encode(self, SOUND_EXPR_STEPS_OFFSET, SOUND_EXPR_STEPS_LENGTH, 128);
225+
sound_expr_encode(self, SOUND_EXPR_FX_PARAM_OFFSET, SOUND_EXPR_FX_PARAM_LENGTH, 1);
226+
sound_expr_encode(self, SOUND_EXPR_FX_STEPS_OFFSET, SOUND_EXPR_FX_STEPS_LENGTH, 24);
227+
228+
// Modify any given parameters.
229+
sound_expr_encode(self, SOUND_EXPR_FREQUENCY_START_OFFSET, SOUND_EXPR_FREQUENCY_START_LENGTH, args[ARG_freq_start].u_int);
230+
sound_expr_encode(self, SOUND_EXPR_FREQUENCY_END_OFFSET, SOUND_EXPR_FREQUENCY_END_LENGTH, args[ARG_freq_end].u_int);
231+
sound_expr_encode(self, SOUND_EXPR_DURATION_OFFSET, SOUND_EXPR_DURATION_LENGTH, args[ARG_duration].u_int);
232+
sound_expr_encode(self, SOUND_EXPR_VOLUME_START_OFFSET, SOUND_EXPR_VOLUME_START_LENGTH, args[ARG_vol_start].u_int);
233+
sound_expr_encode(self, SOUND_EXPR_VOLUME_END_OFFSET, SOUND_EXPR_VOLUME_END_LENGTH, args[ARG_vol_end].u_int);
234+
sound_expr_encode(self, SOUND_EXPR_WAVE_OFFSET, SOUND_EXPR_WAVE_LENGTH, args[ARG_wave].u_int);
235+
sound_expr_encode(self, SOUND_EXPR_FX_CHOICE_OFFSET, SOUND_EXPR_FX_CHOICE_LENGTH, args[ARG_fx].u_int);
236+
sound_expr_encode(self, SOUND_EXPR_SHAPE_OFFSET, SOUND_EXPR_SHAPE_LENGTH, args[ARG_shape].u_int);
237+
238+
// Return new sound effect object
239+
return MP_OBJ_FROM_PTR(self);
240+
}
241+
242+
STATIC void microbit_soundeffect_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
243+
microbit_soundeffect_obj_t *self = MP_OBJ_TO_PTR(self_in);
244+
const soundeffect_attr_t *soundeffect_attr = NULL;
245+
for (size_t i = 0; i < MP_ARRAY_SIZE(soundeffect_attr_table); ++i) {
246+
if (soundeffect_attr_table[i].qst == attr) {
247+
soundeffect_attr = &soundeffect_attr_table[i];
248+
break;
249+
}
250+
}
251+
if (soundeffect_attr == NULL) {
252+
// Invalid attribute, set MP_OBJ_SENTINEL to continue lookup in locals dict.
253+
dest[1] = MP_OBJ_SENTINEL;
254+
return;
255+
}
256+
if (dest[0] == MP_OBJ_NULL) {
257+
// Load attribute.
258+
unsigned int value = sound_expr_decode(self, soundeffect_attr->offset, soundeffect_attr->length);
259+
if (attr == MP_QSTR_fx && value == 0) {
260+
dest[0] = mp_const_none;
261+
} else {
262+
dest[0] = MP_OBJ_NEW_SMALL_INT(value);
263+
}
264+
} else if (dest[1] != MP_OBJ_NULL) {
265+
// Store attribute.
266+
if (self->is_mutable) {
267+
unsigned int value = 0;
268+
if (dest[1] != mp_const_none) {
269+
value = mp_obj_get_int(dest[1]);
270+
}
271+
sound_expr_encode(self, soundeffect_attr->offset, soundeffect_attr->length, value);
272+
dest[0] = MP_OBJ_NULL; // Indicate store succeeded.
273+
}
274+
}
275+
}
276+
277+
STATIC mp_obj_t microbit_soundeffect_from_string(mp_obj_t str_in) {
278+
microbit_soundeffect_obj_t *self = m_new_obj(microbit_soundeffect_obj_t);
279+
self->base.type = &microbit_soundeffect_type;
280+
self->is_mutable = true;
281+
282+
// Initialise the sound expression data with the preset values.
283+
memset(&self->sound_expr[0], '0', SOUND_EXPR_TOTAL_LENGTH);
284+
size_t len;
285+
const char *str = mp_obj_str_get_data(str_in, &len);
286+
if (len > SOUND_EXPR_TOTAL_LENGTH) {
287+
len = SOUND_EXPR_TOTAL_LENGTH;
288+
}
289+
memcpy(&self->sound_expr[0], str, len);
290+
291+
return MP_OBJ_FROM_PTR(self);
292+
}
293+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(microbit_soundeffect_from_string_obj, microbit_soundeffect_from_string);
294+
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(microbit_soundeffect_from_string_staticmethod_obj, MP_ROM_PTR(&microbit_soundeffect_from_string_obj));
295+
296+
STATIC mp_obj_t microbit_soundeffect_copy(mp_obj_t self_in) {
297+
microbit_soundeffect_obj_t *self = MP_OBJ_TO_PTR(self_in);
298+
microbit_soundeffect_obj_t *copy = m_new_obj(microbit_soundeffect_obj_t);
299+
copy->base.type = self->base.type;
300+
copy->is_mutable = true;
301+
memcpy(&copy->sound_expr[0], &self->sound_expr[0], SOUND_EXPR_TOTAL_LENGTH);
302+
303+
return MP_OBJ_FROM_PTR(copy);
304+
}
305+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(microbit_soundeffect_copy_obj, microbit_soundeffect_copy);
306+
307+
STATIC const mp_rom_map_elem_t microbit_soundeffect_locals_dict_table[] = {
308+
// Static methods.
309+
{ MP_ROM_QSTR(MP_QSTR__from_string), MP_ROM_PTR(&microbit_soundeffect_from_string_staticmethod_obj) },
310+
311+
// Instance methods.
312+
{ MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&microbit_soundeffect_copy_obj) },
313+
314+
// Class constants.
315+
#define C(NAME) { MP_ROM_QSTR(MP_QSTR_ ## NAME), MP_ROM_INT(SOUND_EFFECT_ ## NAME) }
316+
317+
C(WAVE_SINE),
318+
C(WAVE_SAWTOOTH),
319+
C(WAVE_TRIANGLE),
320+
C(WAVE_SQUARE),
321+
C(WAVE_NOISE),
322+
323+
C(SHAPE_LINEAR),
324+
C(SHAPE_CURVE),
325+
C(SHAPE_LOG),
326+
327+
C(FX_NONE),
328+
C(FX_TREMOLO),
329+
C(FX_VIBRATO),
330+
C(FX_WARBLE),
331+
332+
#undef C
333+
};
334+
STATIC MP_DEFINE_CONST_DICT(microbit_soundeffect_locals_dict, microbit_soundeffect_locals_dict_table);
335+
336+
const mp_obj_type_t microbit_soundeffect_type = {
337+
{ &mp_type_type },
338+
.name = MP_QSTR_MicroBitSoundEffect,
339+
.print = microbit_soundeffect_print,
340+
.make_new = microbit_soundeffect_make_new,
341+
.attr = microbit_soundeffect_attr,
342+
.locals_dict = (mp_obj_dict_t *)&microbit_soundeffect_locals_dict,
343+
};

0 commit comments

Comments
 (0)