From df79caf34e59f9c62fb3a81b6f1a89a6257e27d8 Mon Sep 17 00:00:00 2001 From: ZodiusInfuser Date: Fri, 26 Aug 2022 19:32:39 +0100 Subject: [PATCH] Exposed remaining AudioChannel parameters --- examples/galactic_unicorn/rainbow_text.cpp | 11 +- libraries/pico_synth/pico_synth.cpp | 14 + libraries/pico_synth/pico_synth.hpp | 5 +- .../feature_test_with_audio.py | 140 ++++----- .../galactic_unicorn/galactic_unicorn.c | 23 +- .../galactic_unicorn/galactic_unicorn.cpp | 278 ++++++++++++++---- .../galactic_unicorn/galactic_unicorn.h | 12 +- 7 files changed, 347 insertions(+), 136 deletions(-) diff --git a/examples/galactic_unicorn/rainbow_text.cpp b/examples/galactic_unicorn/rainbow_text.cpp index fa8d6949..bee8ea9b 100644 --- a/examples/galactic_unicorn/rainbow_text.cpp +++ b/examples/galactic_unicorn/rainbow_text.cpp @@ -68,7 +68,7 @@ int main() { i++; graphics.set_pen(0, 0, 0); graphics.clear(); - + float s = 0.8f;//0.65f + (sin(i / 25.0f) * 0.15f); float a = 1.0f;// (sin(i / 25.0f) * 100.0f); @@ -76,7 +76,7 @@ int main() { float y = (cos((i) / 40.0f) * 5.0f); graphics.set_pen(255, 255, 255); text("Galactic Unicorn", Point(x, y), s, a); - + uint8_t *p = (uint8_t *)graphics.frame_buffer; for(size_t i = 0; i < 53 * 11; i++) { int x = i % 53; @@ -92,14 +92,11 @@ int main() { b = hue_map[x][2]; } - galactic_unicorn.set_pixel(x, y, r, g, b); + graphics.set_pen(r, g, b); + graphics.pixel(Point(x, y)); } - - } - - printf("done\n"); return 0; diff --git a/libraries/pico_synth/pico_synth.cpp b/libraries/pico_synth/pico_synth.cpp index 4fc66cbf..4f96ed6f 100644 --- a/libraries/pico_synth/pico_synth.cpp +++ b/libraries/pico_synth/pico_synth.cpp @@ -56,6 +56,20 @@ namespace pimoroni { adsr_step = 0; } + void AudioChannel::restore() { + // Put all the parameters back to their initial values + waveforms = 0; + frequency = 660; + volume = 0xffff; + + attack_ms = 2; + decay_ms = 6; + sustain = 0xffff; + release_ms = 1; + pulse_width = 0x7fff; + noise = 0; + } + bool PicoSynth::is_audio_playing() { if(volume == 0) { return false; diff --git a/libraries/pico_synth/pico_synth.hpp b/libraries/pico_synth/pico_synth.hpp index f0c48b9a..01fe55bc 100644 --- a/libraries/pico_synth/pico_synth.hpp +++ b/libraries/pico_synth/pico_synth.hpp @@ -69,11 +69,11 @@ namespace pimoroni { struct AudioChannel { uint8_t waveforms = 0; // bitmask for enabled waveforms (see Waveform enum for values) uint16_t frequency = 660; // frequency of the voice (Hz) - uint16_t volume = 0xffff; // channel volume + uint16_t volume = UINT16_MAX; // channel volume uint16_t attack_ms = 2; // attack period (cannot be zero) uint16_t decay_ms = 6; // decay period (cannot be zero) - uint16_t sustain = 0xffff; // sustain volume + uint16_t sustain = UINT16_MAX; // sustain volume uint16_t release_ms = 1; // release period uint16_t pulse_width = 0x7fff; // duty cycle of square wave (default 50%) int16_t noise = 0; // current noise value @@ -101,6 +101,7 @@ namespace pimoroni { void trigger_sustain(); void trigger_release(); void off(); + void restore(); }; class PicoSynth { diff --git a/micropython/examples/galactic_unicorn/feature_test_with_audio.py b/micropython/examples/galactic_unicorn/feature_test_with_audio.py index fe4d81b8..4c32afbe 100644 --- a/micropython/examples/galactic_unicorn/feature_test_with_audio.py +++ b/micropython/examples/galactic_unicorn/feature_test_with_audio.py @@ -712,7 +712,7 @@ def next_beat(): global beat for i in range(5): if notes[i][beat] > 0: - channels[i].freq(notes[i][beat]) + channels[i].frequency(notes[i][beat]) channels[i].trigger_attack() elif notes[i][beat] == -1: channels[i].trigger_release() @@ -736,36 +736,36 @@ while True: if gu.is_pressed(GalacticUnicorn.SWITCH_A): if not was_a_pressed: - channel0.configure(Channel.TRIANGLE + Channel.SQUARE, - 16, - 168, - 0, - 168, - 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) + channel0.configure(waveforms=Channel.TRIANGLE + Channel.SQUARE, + attack=0.016, + decay=0.168, + sustain=0, + release=0.168, + volume=0) + channel1.configure(waveforms=Channel.SINE + Channel.SQUARE, + attack=0.038, + decay=0.300, + sustain=0, + release=0, + volume=12000/65535) + channel2.configure(waveforms=Channel.NOISE, + attack=0.005, + decay=0.010, + sustain=16000/65535, + release=0.100, + volume=0) + channel3.configure(waveforms=Channel.NOISE, + attack=0.005, + decay=0.005, + sustain=8000/65535, + release=0.040, + volume=0) + channel4.configure(waveforms=Channel.SQUARE, + attack=0.010, + decay=0.100, + sustain=0, + release=0.500, + volume=0) if not synthing: beat = 0 next_beat() @@ -797,7 +797,7 @@ while True: timer.deinit() freq_b = 600 - channel1.play_tone(freq_b, 0.06, fade_in=0.5) + channel1.play_tone(freq_b, 0.06, attack=0.5) gu.play_synth() synthing = False @@ -822,57 +822,57 @@ while True: if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP): # gu.adjust_brightness(+0.01) if bool_playing: - freq_b += 10 - channel1.freq(freq_b) + freq_b = min(freq_b + 10, 20000) + channel1.frequency(freq_b) if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN): # gu.adjust_brightness(-0.01) if bool_playing: - freq_b -= 10 - channel1.freq(freq_b) + freq_b = max(freq_b - 10, 10) + channel1.frequency(max(freq_b, 10)) if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_UP): if bool_playing: - freq_a += 10 - channel0.freq(freq_a) + freq_a = min(freq_a + 10, 20000) + channel0.frequency(freq_a) if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_DOWN): if bool_playing: - freq_a -= 10 - channel0.freq(freq_a) + freq_a = max(freq_a - 10, 10) + channel0.frequency(freq_a) if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP): if not was_z_pressed: - 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) + channel0.configure(waveforms=Channel.TRIANGLE + Channel.SQUARE, + attack=0.016, + decay=0.168, + sustain=0xafff/65535, + release=0.168, + volume=10000/65535) + channel1.configure(waveforms=Channel.SINE + Channel.SQUARE, + attack=0.038, + decay=0.300, + sustain=0, + release=0, + volume=12000/65535) + channel2.configure(waveforms=Channel.NOISE, + attack=0.005, + decay=0.010, + sustain=16000/65535, + release=0.100, + volume=18000/65535) + channel3.configure(waveforms=Channel.NOISE, + attack=0.005, + decay=0.005, + sustain=8000/65535, + release=0.040, + volume=8000/65535) + channel4.configure(waveforms=Channel.SQUARE, + attack=0.010, + decay=0.100, + sustain=0, + release=0.500, + volume=12000/65535) 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 e44ca13e..7bb36ffa 100644 --- a/micropython/modules/galactic_unicorn/galactic_unicorn.c +++ b/micropython/modules/galactic_unicorn/galactic_unicorn.c @@ -3,11 +3,20 @@ /***** 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_KW(Channel_configure_obj, 1, Channel_configure); +MP_DEFINE_CONST_FUN_OBJ_1(Channel_restore_obj, Channel_restore); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_waveforms_obj, 1, 2, Channel_waveforms); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_frequency_obj, 1, 2, Channel_frequency); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_volume_obj, 1, 2, Channel_volume); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_attack_duration_obj, 1, 2, Channel_attack_duration); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_decay_duration_obj, 1, 2, Channel_decay_duration); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_sustain_level_obj, 1, 2, Channel_sustain_level); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_release_duration_obj, 1, 2, Channel_release_duration); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Channel_pulse_width_obj, 1, 2, Channel_pulse_width); 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(Channel_stop_playing_obj, Channel_stop_playing); MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn___del___obj, GalacticUnicorn___del__); MP_DEFINE_CONST_FUN_OBJ_1(GalacticUnicorn_clear_obj, GalacticUnicorn_clear); @@ -29,7 +38,15 @@ MP_DEFINE_CONST_FUN_OBJ_2(GalacticUnicorn_synth_channel_obj, GalacticUnicorn_syn 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_restore), MP_ROM_PTR(&Channel_restore_obj) }, + { MP_ROM_QSTR(MP_QSTR_waveforms), MP_ROM_PTR(&Channel_waveforms_obj) }, + { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&Channel_frequency_obj) }, + { MP_ROM_QSTR(MP_QSTR_volume), MP_ROM_PTR(&Channel_volume_obj) }, + { MP_ROM_QSTR(MP_QSTR_attack_duration), MP_ROM_PTR(&Channel_attack_duration_obj) }, + { MP_ROM_QSTR(MP_QSTR_decay_duration), MP_ROM_PTR(&Channel_decay_duration_obj) }, + { MP_ROM_QSTR(MP_QSTR_sustain_level), MP_ROM_PTR(&Channel_sustain_level_obj) }, + { MP_ROM_QSTR(MP_QSTR_release_duration), MP_ROM_PTR(&Channel_release_duration_obj) }, + { MP_ROM_QSTR(MP_QSTR_pulse_width), MP_ROM_PTR(&Channel_pulse_width_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) }, diff --git a/micropython/modules/galactic_unicorn/galactic_unicorn.cpp b/micropython/modules/galactic_unicorn/galactic_unicorn.cpp index 8b724e0f..64790e16 100644 --- a/micropython/modules/galactic_unicorn/galactic_unicorn.cpp +++ b/micropython/modules/galactic_unicorn/galactic_unicorn.cpp @@ -45,17 +45,86 @@ mp_obj_t Channel___del__(mp_obj_t self_in) { } +/***** Helper Functions *****/ +void set_channel_waveforms(AudioChannel& channel, mp_obj_t in) { + int waveforms = mp_obj_get_int(in); + const int mask = (NOISE | SQUARE | SAW | TRIANGLE | SINE | WAVE); + if(waveforms < 0 || (waveforms & mask) == 0) { + mp_raise_ValueError("waveforms invalid. Expected a combination of NOISE, SQUARE, SAW, TRIANGLE, SINE, or WAVE"); + } + channel.waveforms = (uint8_t)waveforms; +} + +void set_channel_frequency(AudioChannel& channel, mp_obj_t in) { + int freq = mp_obj_get_int(in); + if(freq <= 0 || freq > UINT16_MAX) { + mp_raise_ValueError("frequency out of range. Expected greater than 0Hz to 65535Hz"); + } + channel.frequency = (uint16_t)freq; +} + +void set_channel_volume(AudioChannel& channel, mp_obj_t in) { + float volume = mp_obj_get_float(in); + if(volume < 0.0f || volume > 1.0f) { + mp_raise_ValueError("volume out of range. Expected 0.0 to 1.0"); + } + channel.volume = (uint16_t)(volume * UINT16_MAX); +} + +void set_channel_attack(AudioChannel& channel, mp_obj_t in) { + int attack_ms = (int)(mp_obj_get_float(in) * 1000.0f); + if(attack_ms < 0 || attack_ms > UINT16_MAX) { + mp_raise_ValueError("attack out of range. Expected 0.0s to 65.5s"); + } + channel.attack_ms = MAX(attack_ms, 1); +} + +void set_channel_decay(AudioChannel& channel, mp_obj_t in) { + int decay_ms = (int)(mp_obj_get_float(in) * 1000.0f); + if(decay_ms < 0 || decay_ms > UINT16_MAX) { + mp_raise_ValueError("decay out of range. Expected 0.0s to 65.5s"); + } + channel.decay_ms = MAX(decay_ms, 1); +} + +void set_channel_sustain(AudioChannel& channel, mp_obj_t in) { + float sustain = mp_obj_get_float(in); + if(sustain < 0.0f || sustain > 1.0f) { + mp_raise_ValueError("sustain out of range. Expected 0.0 to 1.0"); + } + channel.sustain = (uint16_t)(sustain * UINT16_MAX); +} + +void set_channel_release(AudioChannel& channel, mp_obj_t in) { + int release_ms = (int)(mp_obj_get_float(in) * 1000.0f); + if(release_ms < 0 || release_ms > UINT16_MAX) { + mp_raise_ValueError("release out of range. Expected 0.0s to 65.5s"); + } + channel.release_ms = MAX(release_ms, 1); +} + +void set_channel_pulse_width(AudioChannel& channel, mp_obj_t in) { + float pulse_width = mp_obj_get_float(in); + if(pulse_width < 0.0f || pulse_width > 1.0f) { + mp_raise_ValueError("pulse_width out of range. Expected 0.0 to 1.0"); + } + channel.pulse_width = (uint16_t)(pulse_width * UINT16_MAX); +} + + /***** 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 }; + enum { ARG_self, ARG_waveforms, ARG_frequency, ARG_volume, ARG_attack, ARG_decay, ARG_sustain, ARG_release, ARG_pulse_width }; 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 }, + { MP_QSTR_waveforms, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_frequency, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_volume, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_attack, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_decay, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_sustain, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_release, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_pulse_width, MP_ARG_OBJ, {.u_obj = mp_const_none} } }; // Parse args. @@ -64,25 +133,140 @@ mp_obj_t Channel_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw _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; + mp_obj_t waveforms = args[ARG_waveforms].u_obj; + if(waveforms != mp_const_none) { + set_channel_waveforms(*self->channel, waveforms); + } + + mp_obj_t frequency = args[ARG_frequency].u_obj; + if(frequency != mp_const_none) { + set_channel_frequency(*self->channel, frequency); + } + + mp_obj_t volume = args[ARG_volume].u_obj; + if(volume != mp_const_none) { + set_channel_volume(*self->channel, volume); + } + + mp_obj_t attack = args[ARG_attack].u_obj; + if(attack != mp_const_none) { + set_channel_attack(*self->channel, attack); + } + + mp_obj_t decay = args[ARG_decay].u_obj; + if(decay != mp_const_none) { + set_channel_decay(*self->channel, decay); + } + + mp_obj_t sustain = args[ARG_sustain].u_obj; + if(sustain != mp_const_none) { + set_channel_sustain(*self->channel, sustain); + } + + mp_obj_t release = args[ARG_release].u_obj; + if(release != mp_const_none) { + set_channel_release(*self->channel, release); + } + + mp_obj_t pulse_width = args[ARG_pulse_width].u_obj; + if(pulse_width != mp_const_none) { + set_channel_pulse_width(*self->channel, pulse_width); + } return mp_const_none; } -mp_obj_t Channel_freq(mp_obj_t self_in, mp_obj_t freq_in) { +mp_obj_t Channel_restore(mp_obj_t self_in) { _Channel_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Channel_obj_t); + self->channel->restore(); + return mp_const_none; +} - float freq = mp_obj_get_float(freq_in); - if(freq <= 0.0f) { - mp_raise_ValueError("freq out of range. Expected greater than 0.0"); +mp_obj_t Channel_waveforms(size_t n_args, const mp_obj_t *args) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t); + + if(n_args == 1) { + return mp_obj_new_int(self->channel->waveforms); } - self->channel->frequency = freq; + set_channel_waveforms(*self->channel, args[1]); + return mp_const_none; +} + +mp_obj_t Channel_frequency(size_t n_args, const mp_obj_t *args) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t); + + if(n_args == 1) { + return mp_obj_new_int(self->channel->frequency); + } + + set_channel_frequency(*self->channel, args[1]); + return mp_const_none; +} + +mp_obj_t Channel_volume(size_t n_args, const mp_obj_t *args) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t); + + if(n_args == 1) { + return mp_obj_new_float((float)self->channel->volume / UINT16_MAX); + } + + set_channel_volume(*self->channel, args[1]); + return mp_const_none; +} + +mp_obj_t Channel_attack_duration(size_t n_args, const mp_obj_t *args) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t); + + if(n_args == 1) { + return mp_obj_new_float((float)self->channel->attack_ms / 1000.0f); + } + + set_channel_attack(*self->channel, args[1]); + return mp_const_none; +} + +mp_obj_t Channel_decay_duration(size_t n_args, const mp_obj_t *args) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t); + + if(n_args == 1) { + return mp_obj_new_float((float)self->channel->decay_ms / 1000.0f); + } + + set_channel_decay(*self->channel, args[1]); + return mp_const_none; +} + +mp_obj_t Channel_sustain_level(size_t n_args, const mp_obj_t *args) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t); + + if(n_args == 1) { + return mp_obj_new_float((float)self->channel->sustain / UINT16_MAX); + } + + set_channel_sustain(*self->channel, args[1]); + return mp_const_none; +} + +mp_obj_t Channel_release_duration(size_t n_args, const mp_obj_t *args) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t); + + if(n_args == 1) { + return mp_obj_new_float((float)self->channel->release_ms / 1000.0f); + } + + set_channel_release(*self->channel, args[1]); + return mp_const_none; +} + +mp_obj_t Channel_pulse_width(size_t n_args, const mp_obj_t *args) { + _Channel_obj_t *self = MP_OBJ_TO_PTR2(args[0], _Channel_obj_t); + + if(n_args == 1) { + return mp_obj_new_float((float)self->channel->pulse_width / 0xffff); + } + + set_channel_pulse_width(*self->channel, args[1]); return mp_const_none; } @@ -104,10 +288,10 @@ mp_obj_t Channel_play_tone(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw 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_frequency, 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} }, + { MP_QSTR_attack, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_release, MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; // Parse args. @@ -116,45 +300,35 @@ mp_obj_t Channel_play_tone(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw _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"); + set_channel_frequency(*self->channel, args[ARG_freq].u_obj); + + mp_obj_t volume = args[ARG_volume].u_obj; + if(volume != mp_const_none) { + set_channel_volume(*self->channel, volume); + } + else { + self->channel->volume = UINT16_MAX; } - 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"); - } + mp_obj_t attack_ms = args[ARG_fade_in].u_obj; + if(attack_ms != mp_const_none) { + set_channel_attack(*self->channel, attack_ms); + } + else { + self->channel->attack_ms = 1; } - 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); + mp_obj_t release_ms = args[ARG_fade_out].u_obj; + if(release_ms != mp_const_none) { + set_channel_release(*self->channel, release_ms); + } + else { + self->channel->release_ms = 1; } - 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->sustain = UINT16_MAX; self->channel->trigger_attack(); diff --git a/micropython/modules/galactic_unicorn/galactic_unicorn.h b/micropython/modules/galactic_unicorn/galactic_unicorn.h index 11f0993d..21859574 100644 --- a/micropython/modules/galactic_unicorn/galactic_unicorn.h +++ b/micropython/modules/galactic_unicorn/galactic_unicorn.h @@ -10,11 +10,19 @@ extern void Channel_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki 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_restore(mp_obj_t self_in); +extern mp_obj_t Channel_waveforms(size_t n_args, const mp_obj_t *args); +extern mp_obj_t Channel_frequency(size_t n_args, const mp_obj_t *args); +extern mp_obj_t Channel_volume(size_t n_args, const mp_obj_t *args); +extern mp_obj_t Channel_attack_duration(size_t n_args, const mp_obj_t *args); +extern mp_obj_t Channel_decay_duration(size_t n_args, const mp_obj_t *args); +extern mp_obj_t Channel_sustain_level(size_t n_args, const mp_obj_t *args); +extern mp_obj_t Channel_release_duration(size_t n_args, const mp_obj_t *args); +extern mp_obj_t Channel_pulse_width(size_t n_args, const mp_obj_t *args); 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 mp_obj_t Channel_stop_playing(mp_obj_t self_in); 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);