From 53dfb9866d98c5ac8cd2d16dc93cc4261994c55d Mon Sep 17 00:00:00 2001 From: ZodiusInfuser Date: Thu, 25 Aug 2022 17:27:53 +0100 Subject: [PATCH] Started splitting out synth into separate channel objects --- .../galactic_unicorn/galactic_unicorn.cpp | 5 + .../galactic_unicorn/galactic_unicorn.hpp | 3 +- .../feature_test_with_audio.py | 165 ++++++-------- .../galactic_unicorn/galactic_unicorn.c | 50 +++-- .../galactic_unicorn/galactic_unicorn.cpp | 207 ++++++++++++++---- .../galactic_unicorn/galactic_unicorn.h | 16 +- 6 files changed, 287 insertions(+), 159 deletions(-) diff --git a/libraries/galactic_unicorn/galactic_unicorn.cpp b/libraries/galactic_unicorn/galactic_unicorn.cpp index 333debef..66830313 100644 --- a/libraries/galactic_unicorn/galactic_unicorn.cpp +++ b/libraries/galactic_unicorn/galactic_unicorn.cpp @@ -444,6 +444,11 @@ namespace pimoroni { } } + AudioChannel& GalacticUnicorn::synth_channel(uint channel) { + assert(channel < PicoSynth::CHANNEL_COUNT); + return synth.channels[channel]; + } + void GalacticUnicorn::set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) { if(x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return; diff --git a/libraries/galactic_unicorn/galactic_unicorn.hpp b/libraries/galactic_unicorn/galactic_unicorn.hpp index f1d4f9d8..00e52439 100644 --- a/libraries/galactic_unicorn/galactic_unicorn.hpp +++ b/libraries/galactic_unicorn/galactic_unicorn.hpp @@ -75,10 +75,8 @@ namespace pimoroni { int16_t tone_buffers[NUM_TONE_BUFFERS][TONE_BUFFER_SIZE] = {0}; uint current_buffer = 0; - public: PicoSynth synth; - private: enum PlayMode { PLAYING_BUFFER, //PLAYING_TONE, @@ -116,6 +114,7 @@ namespace pimoroni { void play_sample(uint8_t *data, uint32_t length); void play_synth(); void stop_playing(); + AudioChannel& synth_channel(uint channel); private: void next_bitstream_sequence(); diff --git a/micropython/examples/galactic_unicorn/feature_test_with_audio.py b/micropython/examples/galactic_unicorn/feature_test_with_audio.py index a83290f7..2ded25cc 100644 --- a/micropython/examples/galactic_unicorn/feature_test_with_audio.py +++ b/micropython/examples/galactic_unicorn/feature_test_with_audio.py @@ -2,7 +2,7 @@ import gc import time import math from machine import Timer -from galactic import GalacticUnicorn +from galactic import GalacticUnicorn, Channel from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY gc.collect() @@ -700,15 +700,22 @@ freq_b = 0 beat = 0 +channel0 = gu.synth_channel(0) +channel1 = gu.synth_channel(1) +channel2 = gu.synth_channel(2) +channel3 = gu.synth_channel(3) +channel4 = gu.synth_channel(7) +channels = [channel0, channel1, channel2, channel3, channel4] + def next_beat(): global beat for i in range(5): if notes[i][beat] > 0: - gu.channel_freq(i, notes[i][beat]) - gu.channel_trigger_attack(i) + channels[i].freq(notes[i][beat]) + channels[i].trigger_attack() elif notes[i][beat] == -1: - gu.channel_trigger_release(i) + channels[i].trigger_release() beat = (beat + 1) % SONG_LENGTH @@ -729,36 +736,36 @@ while True: if gu.is_pressed(GalacticUnicorn.SWITCH_A): if not was_a_pressed: - gu.channel_configure(0, gu.WF_TRIANGLE + gu.WF_SQUARE, - 16, + channel0.configure(Channel.TRIANGLE + Channel.SQUARE, + 16, 168, 0, 168, 0) - gu.channel_configure(1, gu.WF_SINE + gu.WF_SQUARE, - 38, - 300, - 0, - 0, - 12000) - gu.channel_configure(2, gu.WF_NOISE, - 5, - 10, - 16000, - 100, - 0) - gu.channel_configure(3, gu.WF_NOISE, - 5, - 5, - 8000, - 40, - 0) - gu.channel_configure(4, gu.WF_SQUARE, - 10, - 100, - 0, - 500, - 0) + channel1.configure(Channel.SINE + Channel.SQUARE, + 38, + 300, + 0, + 0, + 12000) + channel2.configure(Channel.NOISE, + 5, + 10, + 16000, + 100, + 0) + channel3.configure(Channel.NOISE, + 5, + 5, + 8000, + 40, + 0) + channel4.configure(Channel.SQUARE, + 10, + 100, + 0, + 500, + 0) if not synthing: beat = 0 next_beat() @@ -774,21 +781,8 @@ while True: if not was_b_pressed: timer.deinit() freq_a = 400 + channel0.play_tone(freq_a, 0.06) - gu.channel_freq(0, freq_a) - gu.channel_configure(0, gu.WF_SINE, - 1, - 1, - 0xffff, - 1, - 4000) - gu.channel_configure(1, gu.WF_SINE, - 1, - 1, - 0xffff, - 1, - 4000) - gu.channel_trigger_attack(0) gu.play_synth() synthing = False @@ -803,21 +797,8 @@ while True: timer.deinit() freq_b = 600 - gu.channel_freq(1, freq_b) - gu.channel_configure(0, gu.WF_SINE, - 1, - 1, - 0xffff, - 1, - 4000) - gu.channel_configure(1, gu.WF_SINE, - 1, - 1, - 0xffff, - 1, - 4000) + channel1.play_tone(freq_b, 0.06, fade_in=0.5) - gu.channel_trigger_attack(1) gu.play_synth() synthing = False @@ -842,56 +823,56 @@ while True: # gu.adjust_brightness(+0.01) if bool_playing: freq_b += 10 - gu.channel_freq(1, freq_b) + channel1.freq(freq_b) if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN): # gu.adjust_brightness(-0.01) if bool_playing: freq_b -= 10 - gu.channel_freq(1, freq_b) + channel1.freq(freq_b) if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_UP): if bool_playing: freq_a += 10 - gu.channel_freq(0, freq_a) + channel0.freq(freq_a) if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_DOWN): if bool_playing: freq_a -= 10 - gu.channel_freq(0, freq_a) + channel0.freq(freq_a) if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP): if not was_z_pressed: - gu.channel_configure(0, gu.WF_TRIANGLE + gu.WF_SQUARE, - 16, - 168, - 0xafff, - 168, - 10000) - gu.channel_configure(1, gu.WF_SINE + gu.WF_SQUARE, - 38, - 300, - 0, - 0, - 12000) - gu.channel_configure(2, gu.WF_NOISE, - 5, - 10, - 16000, - 100, - 18000) - gu.channel_configure(3, gu.WF_NOISE, - 5, - 5, - 8000, - 40, - 8000) - gu.channel_configure(4, gu.WF_SQUARE, - 10, - 100, - 0, - 500, - 12000) + channel0.configure(Channel.TRIANGLE + Channel.SQUARE, + 16, + 168, + 0xafff, + 168, + 10000) + channel1.configure(Channel.SINE + Channel.SQUARE, + 38, + 300, + 0, + 0, + 12000) + channel2.configure(Channel.NOISE, + 5, + 10, + 16000, + 100, + 18000) + channel3.configure(Channel.NOISE, + 5, + 5, + 8000, + 40, + 8000) + channel4.configure(Channel.SQUARE, + 10, + 100, + 0, + 500, + 12000) if not synthing: beat = 0 next_beat() diff --git a/micropython/modules/galactic_unicorn/galactic_unicorn.c b/micropython/modules/galactic_unicorn/galactic_unicorn.c index 8fded8d6..e44ca13e 100644 --- a/micropython/modules/galactic_unicorn/galactic_unicorn.c +++ b/micropython/modules/galactic_unicorn/galactic_unicorn.c @@ -2,6 +2,13 @@ /***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_1(Channel___del___obj, Channel___del__); +MP_DEFINE_CONST_FUN_OBJ_KW(Channel_configure_obj, 6, Channel_configure); +MP_DEFINE_CONST_FUN_OBJ_2(Channel_freq_obj, Channel_freq); +MP_DEFINE_CONST_FUN_OBJ_1(Channel_trigger_attack_obj, Channel_trigger_attack); +MP_DEFINE_CONST_FUN_OBJ_1(Channel_trigger_release_obj, Channel_trigger_release); +MP_DEFINE_CONST_FUN_OBJ_KW(Channel_play_tone_obj, 2, Channel_play_tone); + MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn___del___obj, GalacticUnicorn___del__); MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_clear_obj, GalacticUnicorn_clear); MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_update_obj, GalacticUnicorn_update); @@ -16,12 +23,25 @@ MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_is_pressed_obj, GalacticUnicorn_is_pre MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_play_sample_obj, GalacticUnicorn_play_sample); MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_play_synth_obj, GalacticUnicorn_play_synth); MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_stop_playing_obj, GalacticUnicorn_stop_playing); -MP_DEFINE_CONST_FUN_OBJ_KW(GalacticUnicorn_channel_configure_obj, 7, GalacticUnicorn_channel_configure); -MP_DEFINE_CONST_FUN_OBJ_3(GalacticUnicorn_channel_freq_obj, GalacticUnicorn_channel_freq); -MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_channel_trigger_attack_obj, GalacticUnicorn_channel_trigger_attack); -MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_channel_trigger_release_obj, GalacticUnicorn_channel_trigger_release); +MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_synth_channel_obj, GalacticUnicorn_synth_channel); /***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t Channel_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Channel___del___obj) }, + { MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&Channel_configure_obj) }, + { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&Channel_freq_obj) }, + { MP_ROM_QSTR(MP_QSTR_trigger_attack), MP_ROM_PTR(&Channel_trigger_attack_obj) }, + { MP_ROM_QSTR(MP_QSTR_trigger_release), MP_ROM_PTR(&Channel_trigger_release_obj) }, + { MP_ROM_QSTR(MP_QSTR_play_tone), MP_ROM_PTR(&Channel_play_tone_obj) }, + + { MP_ROM_QSTR(MP_QSTR_NOISE), MP_ROM_INT(128) }, + { MP_ROM_QSTR(MP_QSTR_SQUARE), MP_ROM_INT(64) }, + { MP_ROM_QSTR(MP_QSTR_SAW), MP_ROM_INT(32) }, + { MP_ROM_QSTR(MP_QSTR_TRIANGLE), MP_ROM_INT(16) }, + { MP_ROM_QSTR(MP_QSTR_SINE), MP_ROM_INT(8) }, + { MP_ROM_QSTR(MP_QSTR_WAVE), MP_ROM_INT(1) }, +}; + STATIC const mp_rom_map_elem_t GalacticUnicorn_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&GalacticUnicorn___del___obj) }, { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&GalacticUnicorn_clear_obj) }, @@ -37,10 +57,7 @@ STATIC const mp_rom_map_elem_t GalacticUnicorn_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_play_sample), MP_ROM_PTR(&GalacticUnicorn_play_sample_obj) }, { MP_ROM_QSTR(MP_QSTR_play_synth), MP_ROM_PTR(&GalacticUnicorn_play_synth_obj) }, { MP_ROM_QSTR(MP_QSTR_stop_playing), MP_ROM_PTR(&GalacticUnicorn_stop_playing_obj) }, - { MP_ROM_QSTR(MP_QSTR_channel_configure), MP_ROM_PTR(&GalacticUnicorn_channel_configure_obj) }, - { MP_ROM_QSTR(MP_QSTR_channel_freq), MP_ROM_PTR(&GalacticUnicorn_channel_freq_obj) }, - { MP_ROM_QSTR(MP_QSTR_channel_trigger_attack), MP_ROM_PTR(&GalacticUnicorn_channel_trigger_attack_obj) }, - { MP_ROM_QSTR(MP_QSTR_channel_trigger_release), MP_ROM_PTR(&GalacticUnicorn_channel_trigger_release_obj) }, + { MP_ROM_QSTR(MP_QSTR_synth_channel), MP_ROM_PTR(&GalacticUnicorn_synth_channel_obj) }, { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(53) }, { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(11) }, @@ -54,18 +71,20 @@ STATIC const mp_rom_map_elem_t GalacticUnicorn_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_SWITCH_VOLUME_DOWN), MP_ROM_INT(8) }, { MP_ROM_QSTR(MP_QSTR_SWITCH_BRIGHTNESS_UP), MP_ROM_INT(21) }, { MP_ROM_QSTR(MP_QSTR_SWITCH_BRIGHTNESS_DOWN), MP_ROM_INT(26) }, - - { MP_ROM_QSTR(MP_QSTR_WF_NOISE), MP_ROM_INT(128) }, - { MP_ROM_QSTR(MP_QSTR_WF_SQUARE), MP_ROM_INT(64) }, - { MP_ROM_QSTR(MP_QSTR_WF_SAW), MP_ROM_INT(32) }, - { MP_ROM_QSTR(MP_QSTR_WF_TRIANGLE), MP_ROM_INT(16) }, - { MP_ROM_QSTR(MP_QSTR_WF_SINE), MP_ROM_INT(8) }, - { MP_ROM_QSTR(MP_QSTR_WF_WAVE), MP_ROM_INT(1) }, }; +STATIC MP_DEFINE_CONST_DICT(Channel_locals_dict, Channel_locals_dict_table); STATIC MP_DEFINE_CONST_DICT(GalacticUnicorn_locals_dict, GalacticUnicorn_locals_dict_table); /***** Class Definition *****/ +const mp_obj_type_t Channel_type = { + { &mp_type_type }, + .name = MP_QSTR_Channel, + .print = Channel_print, + .make_new = Channel_make_new, + .locals_dict = (mp_obj_dict_t*)&Channel_locals_dict, +}; + const mp_obj_type_t GalacticUnicorn_type = { { &mp_type_type }, .name = MP_QSTR_GalacticUnicorn, @@ -77,6 +96,7 @@ const mp_obj_type_t GalacticUnicorn_type = { /***** Globals Table *****/ STATIC const mp_map_elem_t galactic_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_galactic) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_Channel), (mp_obj_t)&Channel_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_GalacticUnicorn), (mp_obj_t)&GalacticUnicorn_type }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_galactic_globals, galactic_globals_table); diff --git a/micropython/modules/galactic_unicorn/galactic_unicorn.cpp b/micropython/modules/galactic_unicorn/galactic_unicorn.cpp index 34d6e4c3..8b724e0f 100644 --- a/micropython/modules/galactic_unicorn/galactic_unicorn.cpp +++ b/micropython/modules/galactic_unicorn/galactic_unicorn.cpp @@ -13,6 +13,155 @@ extern "C" { #include "py/builtin.h" +/********** Channel **********/ + +/***** Variables Struct *****/ +typedef struct _Channel_obj_t { + mp_obj_base_t base; + AudioChannel* channel; +} _Channel_obj_t; + + +/***** Print *****/ +void Channel_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; //Unused input parameter + //_Channel_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Channel_obj_t); + //AudioChannel* channel = self->channel; + mp_print_str(print, "Channel("); + mp_print_str(print, ")"); +} + + +/***** Constructor *****/ +mp_obj_t Channel_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_raise_msg(&mp_type_RuntimeError, "Cannot create Channel objects. They can only be accessed from GalacticUnicorn.synth_channel()"); + return mp_const_none; +} + + +/***** Destructor ******/ +mp_obj_t Channel___del__(mp_obj_t self_in) { + return mp_const_none; +} + + +/***** Methods *****/ +mp_obj_t Channel_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_waveforms, ARG_attack_ms, ARG_decay_ms, ARG_sustain, ARG_release_ms, ARG_volume }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_waveforms, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_attack_ms, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_decay_ms, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_sustain, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_release_ms, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_volumes, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Channel_obj_t); + + self->channel->waveforms = args[ARG_waveforms].u_int; + self->channel->attack_ms = args[ARG_attack_ms].u_int; + self->channel->decay_ms = args[ARG_decay_ms].u_int; + self->channel->sustain = args[ARG_sustain].u_int; + self->channel->release_ms = args[ARG_release_ms].u_int; + self->channel->volume = args[ARG_volume].u_int; + + return mp_const_none; +} + +mp_obj_t Channel_freq(mp_obj_t self_in, mp_obj_t freq_in) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Channel_obj_t); + + float freq = mp_obj_get_float(freq_in); + if(freq <= 0.0f) { + mp_raise_ValueError("freq out of range. Expected greater than 0.0"); + } + self->channel->frequency = freq; + + return mp_const_none; +} + +mp_obj_t Channel_trigger_attack(mp_obj_t self_in) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Channel_obj_t); + self->channel->trigger_attack(); + + return mp_const_none; +} + +mp_obj_t Channel_trigger_release(mp_obj_t self_in) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Channel_obj_t); + self->channel->trigger_release(); + + return mp_const_none; +} + +mp_obj_t Channel_play_tone(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_freq, ARG_volume, ARG_fade_in, ARG_fade_out }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_freq, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_volume, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_fade_in, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_fade_out, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Channel_obj_t); + + float freq = mp_obj_get_float(args[ARG_freq].u_obj); + if(freq <= 0.0f) { + mp_raise_ValueError("freq out of range. Expected greater than 0.0"); + } + + float volume = 1.0f; + if(args[ARG_volume].u_obj != mp_const_none) { + volume = mp_obj_get_float(args[ARG_volume].u_obj); + if(volume < 0.0f || volume > 1.0f) { + mp_raise_ValueError("volume out of range. Expected 0.0 to 1.0"); + } + } + + int fade_in_ms = 1; + if(args[ARG_fade_in].u_obj != mp_const_none) { + float fade_in = mp_obj_get_float(args[ARG_fade_in].u_obj); + if(fade_in <= 0.0f) { + mp_raise_ValueError("fade_in out of range. Expected greater than 0.0"); + } + fade_in_ms = (uint16_t)(fade_in * 1000.0f); + } + + int fade_out_ms = 1; + if(args[ARG_fade_out].u_obj != mp_const_none) { + float fade_out = mp_obj_get_float(args[ARG_fade_out].u_obj); + if(fade_out <= 0.0f) { + mp_raise_ValueError("fade_out out of range. Expected greater than 0.0"); + } + fade_out_ms = (uint16_t)(fade_out * 1000.0f); + } + + + self->channel->frequency = freq; + self->channel->waveforms = Waveform::SINE; + self->channel->attack_ms = MAX(fade_in_ms, 1); + self->channel->decay_ms = 1; + self->channel->sustain = 0xffff; + self->channel->release_ms = MAX(fade_out_ms, 1); + self->channel->volume = (uint16_t)(volume * 0xffff); + + self->channel->trigger_attack(); + + return mp_const_none; +} + + /********** GalacticUnicorn **********/ /***** Variables Struct *****/ @@ -173,55 +322,21 @@ extern mp_obj_t GalacticUnicorn_stop_playing(mp_obj_t self_in) { return mp_const_none; } -extern mp_obj_t GalacticUnicorn_channel_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_channel, ARG_waveforms, ARG_attack_ms, ARG_decay_ms, ARG_sustain, ARG_release_ms, ARG_volume }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_channel, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_waveforms, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_attack_ms, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_decay_ms, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_sustain, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_release_ms, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_volumes, MP_ARG_REQUIRED | MP_ARG_INT }, - }; - - // Parse args. - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - _GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _GalacticUnicorn_obj_t); - - int c = args[ARG_channel].u_int; - self->galactic->synth.channels[c].waveforms = args[ARG_waveforms].u_int; - self->galactic->synth.channels[c].attack_ms = args[ARG_attack_ms].u_int; - self->galactic->synth.channels[c].decay_ms = args[ARG_decay_ms].u_int; - self->galactic->synth.channels[c].sustain = args[ARG_sustain].u_int; - self->galactic->synth.channels[c].release_ms = args[ARG_release_ms].u_int; - self->galactic->synth.channels[c].volume = args[ARG_volume].u_int; - - return mp_const_none; -} - -extern mp_obj_t GalacticUnicorn_channel_freq(mp_obj_t self_in, mp_obj_t channel, mp_obj_t freq) { +extern mp_obj_t GalacticUnicorn_synth_channel(mp_obj_t self_in, mp_obj_t channel_in) { _GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t); - self->galactic->synth.channels[mp_obj_get_int(channel)].frequency = mp_obj_get_float(freq); + // Check that the channel is valid + int channel = mp_obj_get_int(channel_in); + if(channel < 0 || channel >= (int)PicoSynth::CHANNEL_COUNT) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("channel out of range. Expected 0 to %d"), PicoSynth::CHANNEL_COUNT - 1); + } - return mp_const_none; -} + // NOTE This seems to work, in that it give MP access to the calibration object + // Could very easily mess up in weird ways once object deletion is considered? + _Channel_obj_t *channel_obj = m_new_obj_with_finaliser(_Channel_obj_t); + channel_obj->base.type = &Channel_type; + channel_obj->channel = &self->galactic->synth_channel(channel); -extern mp_obj_t GalacticUnicorn_channel_trigger_attack(mp_obj_t self_in, mp_obj_t channel) { - _GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t); - self->galactic->synth.channels[mp_obj_get_int(channel)].trigger_attack(); - - return mp_const_none; -} - -extern mp_obj_t GalacticUnicorn_channel_trigger_release(mp_obj_t self_in, mp_obj_t channel) { - _GalacticUnicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, _GalacticUnicorn_obj_t); - self->galactic->synth.channels[mp_obj_get_int(channel)].trigger_release(); - - return mp_const_none; + return MP_OBJ_FROM_PTR(channel_obj); } } diff --git a/micropython/modules/galactic_unicorn/galactic_unicorn.h b/micropython/modules/galactic_unicorn/galactic_unicorn.h index 69afbd84..11f0993d 100644 --- a/micropython/modules/galactic_unicorn/galactic_unicorn.h +++ b/micropython/modules/galactic_unicorn/galactic_unicorn.h @@ -2,9 +2,20 @@ #include "py/runtime.h" /***** Extern of Class Definition *****/ +extern const mp_obj_type_t Channel_type; extern const mp_obj_type_t GalacticUnicorn_type; /***** Extern of Class Methods *****/ +extern void Channel_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +extern mp_obj_t Channel_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern mp_obj_t Channel___del__(mp_obj_t self_in); +extern mp_obj_t Channel_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t Channel_freq(mp_obj_t self_in, mp_obj_t freq_in); +extern mp_obj_t Channel_trigger_attack(mp_obj_t self_in); +extern mp_obj_t Channel_trigger_release(mp_obj_t self_in); +extern mp_obj_t Channel_play_tone(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + + extern void GalacticUnicorn_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); extern mp_obj_t GalacticUnicorn_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); extern mp_obj_t GalacticUnicorn___del__(mp_obj_t self_in); @@ -28,7 +39,4 @@ extern mp_obj_t GalacticUnicorn_play_sample(mp_obj_t self_in, mp_obj_t data); extern mp_obj_t GalacticUnicorn_play_synth(mp_obj_t self_in); extern mp_obj_t GalacticUnicorn_stop_playing(mp_obj_t self_in); -extern mp_obj_t GalacticUnicorn_channel_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);; -extern mp_obj_t GalacticUnicorn_channel_freq(mp_obj_t self_in, mp_obj_t channel, mp_obj_t freq); -extern mp_obj_t GalacticUnicorn_channel_trigger_attack(mp_obj_t self_in, mp_obj_t channel); -extern mp_obj_t GalacticUnicorn_channel_trigger_release(mp_obj_t self_in, mp_obj_t channel); \ No newline at end of file +extern mp_obj_t GalacticUnicorn_synth_channel(mp_obj_t self_in, mp_obj_t channel_in); \ No newline at end of file