diff --git a/README.md b/README.md index 78150a0af9a5b5043b678192709044affc783e22..66d022cfaf1cc73b6eeee3dd46ae5e358c1751d8 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,33 @@ plays an infinite scripted note sequence and provides a basic micropython repl makes extra noise when Crtl+D'd in the repl (importing py libraries works too) +some fun commands to try: +''' +import badge_audio +#turn on sound +badge_audio.set_global_volume_dB(-10) +#turn off all demo oscillators +badge_audio.dump_all_sources() + +import synth +a=synth.tinysynth(440,1); +a.start(); +b=synth.tinysynth(660,0); +b.start(); +b.stop(); + +#tiny issue with garbage collect: +badge_audio.count_sources(); +a.__del__(); +badge_audio.count_sources(); +import gc +del b +gc.collect() +badge_audio.count_sources(); +#...don't know how to hook up gc to __del__, maybe wrong approach + +''' + ## how to build 1. install esp-idf v4.4: @@ -51,7 +78,7 @@ $ picocom -b 115200 /dev/ttyACM0 ## how to modify -general info +### general info global + micropython entry point: app_main() in micropython/ports/esp32/main.c (includes badge23/espan.h) c entry point, called by^: os_app_main() in badge23/espan.c register new c files for compilation: add to set(BADGE23_LIB) in micropython/ports/esp32/main/CMakelists.txt diff --git a/badge23/audio.c b/badge23/audio.c index f7952636aff3edfe32eae92d4d0af7f33f7b1063..b71d09f92b3ef1cf01bf8df344d8d97b74f9a20d 100644 --- a/badge23/audio.c +++ b/badge23/audio.c @@ -10,6 +10,8 @@ #include <stdio.h> #include <math.h> #include <string.h> +#include "../../py/mphal.h" + #define DRUMS_TOP 0 @@ -22,51 +24,6 @@ static void audio_player_task(void* arg); #define DMA_BUFFER_COUNT 2 #define I2S_PORT 0 -#if 0 -static i2s_chan_handle_t tx_chan; // I2S tx channel handler -static i2s_chan_handle_t rx_chan; // I2S rx channel handler - -static void i2s_init_std_duplex(void) -{ - /* Setp 1: Determine the I2S channel configuration and allocate both channels - * The default configuration can be generated by the helper macro, - * it only requires the I2S controller id and I2S role */ - i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER); - chan_cfg.dma_desc_num = DMA_BUFFER_COUNT; - chan_cfg.dma_frame_num = DMA_BUFFER_SIZE; - - // Play silence after all DMA buffers are empty - chan_cfg.auto_clear = true; - - ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan)); - - /* Step 2: Setting the configurations of standard mode, and initialize rx & tx channels - * The slot configuration and clock configuration can be generated by the macros - * These two helper macros is defined in 'i2s_std.h' which can only be used in STD mode. - * They can help to specify the slot and clock configurations for initialization or re-configuring */ - i2s_std_config_t std_cfg = { - .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE), - //.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), - .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), - .gpio_cfg = { - .mclk = 11, - .bclk = 13, - .ws = 12, - .dout = 14, - .din = I2S_GPIO_UNUSED, - .invert_flags = { - .mclk_inv = false, - .bclk_inv = false, - .ws_inv = false, - }, - }, - }; - /* Initialize the channels */ - ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg)); - ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg)); -} -#endif - static void i2s_init_idk_lol(void){ static const i2s_config_t i2s_config = { @@ -123,6 +80,69 @@ static float FREQ_TABLE[] = { }; #endif +typedef struct _audio_source_t{ + void * render_data; + float (* render_function)(void *); + uint16_t index; + struct _audio_source_t * next; +} audio_source_t; + +static audio_source_t * _audio_sources = NULL; + +uint16_t add_audio_source(void * render_data, void * render_function){ + audio_source_t * src = malloc(sizeof(audio_source_t)); + if(src == NULL) return; + src->render_data = render_data; + src->render_function = render_function; + src->next = NULL; + src->index = 0; + + audio_source_t * audio_source = _audio_sources; + if(audio_source == NULL){ + + _audio_sources = src; + } + while(audio_source != NULL){ + src->index++; + if(audio_source->next == NULL){ + audio_source->next = src; + break; + } else { + audio_source = audio_source->next; + } + } + return src->index; +} + +void remove_audio_source(uint16_t index){ + audio_source_t * audio_source = _audio_sources; + audio_source_t * start_gap = NULL; + + while(audio_source != NULL){ + if(index == audio_source->index){ + if(start_gap == NULL){ + _audio_sources = audio_source->next; + } else { + start_gap->next = audio_source->next; + } + free(audio_source); + break; + } + start_gap = audio_source; + audio_source = audio_source->next; + } +} + +uint16_t count_audio_sources(){ + uint16_t ret = 0; + audio_source_t * audio_source = _audio_sources; + while(audio_source != NULL){ + audio_source = audio_source->next; + ret++; + } + return ret; +} + static void _audio_init(void) { init_scope(241); //i2s_init_std_duplex(); @@ -171,8 +191,8 @@ static void _audio_init(void) { synths[i].waveform = 8; synths[i].noise_reg = 1; } + add_audio_source(&(synths[i]), trad_osc); } - TaskHandle_t handle; xTaskCreate(&audio_player_task, "Audio player", 20000, NULL, configMAX_PRIORITIES - 1, &handle); } @@ -182,6 +202,7 @@ static void _audio_init(void) { #define NAT_LOG_DB 0.1151292546497023 static uint16_t _global_vol = 3000; +static void * _extra_synth = NULL; void set_global_vol_dB(int8_t vol_dB){ if(vol_dB < (BADGE_MIN_VOLUME_DB)){ @@ -194,6 +215,14 @@ void set_global_vol_dB(int8_t vol_dB){ } } +void set_extra_synth(void * synth){ + _extra_synth = synth; +} + +void clear_extra_synth(){ + _extra_synth = NULL; +} + static void audio_player_task(void* arg) { int16_t buffer[DMA_BUFFER_SIZE * 2]; //memset(buffer, 0, sizeof(buffer)); @@ -202,9 +231,19 @@ static void audio_player_task(void* arg) { for(int i = 0; i < (DMA_BUFFER_SIZE * 2); i += 2){ float sample = 0; + /* for(int j = 0; j<NUM_SYNTH; j++){ sample += trad_osc(&(synths[j])); } + if(_extra_synth != NULL){ + sample += trad_osc((trad_osc_t * )_extra_synth); + } + */ + audio_source_t * audio_source = _audio_sources; + while(audio_source != NULL){ + sample += (*(audio_source->render_function))(audio_source->render_data); + audio_source = audio_source->next; + } write_to_scope((int16_t) (1600. * sample)); sample = _global_vol * sample; if(sample > 32767) sample = 32767; diff --git a/badge23/audio.h b/badge23/audio.h index f6586c526fab5fc1f54eca2b31770edd00d3cc74..dddb44cfe6166edb5bc30345396e43e0aa2f8021 100644 --- a/badge23/audio.h +++ b/badge23/audio.h @@ -17,4 +17,9 @@ void synth_fullstop(int i); float synth_get_env(int i); void set_global_vol_dB(int8_t vol_dB); +void set_extra_synth(void * synth); +void clear_extra_synth(); +uint16_t count_audio_sources(); +uint16_t add_audio_source(void * render_data, void * render_function); +void remove_audio_source(uint16_t index); diff --git a/badge23/espan.c b/badge23/espan.c index d7169d03bab5dd3f13e9042ada00acb21123729b..19dca30bd9c949bcfeb2310b9cd4f710abb94005 100644 --- a/badge23/espan.c +++ b/badge23/espan.c @@ -95,7 +95,7 @@ void os_app_main(void) ESP_LOGI(TAG, "I2C initialized successfully"); vTaskDelay(1000 / portTICK_PERIOD_MS); - set_global_vol_dB(0); + set_global_vol_dB(-90); audio_init(); leds_init(); diff --git a/micropython/ports/esp32/badge23_mp_audio.c b/micropython/ports/esp32/badge23_mp_audio.c new file mode 100644 index 0000000000000000000000000000000000000000..c6aa49277e3249c7db9739d0c7cbab3f43fda2fa --- /dev/null +++ b/micropython/ports/esp32/badge23_mp_audio.c @@ -0,0 +1,54 @@ +// probably doesn't need all of these idk +#include <stdio.h> +#include <string.h> + +#include "py/runtime.h" +#include "py/mphal.h" +#include "mphalport.h" +#include "modmachine.h" +#include "extmod/virtpin.h" +#include "machine_rtc.h" +#include "py/builtin.h" +#include "py/runtime.h" +#include "../badge23/audio.h" + +STATIC mp_obj_t mp_set_global_volume_dB(size_t n_args, const mp_obj_t *args) { + mp_float_t x = mp_obj_get_float(args[0]); + int8_t d = x; + set_global_vol_dB(d); + mp_float_t l = x; + return mp_obj_new_float(l); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_set_global_volume_dB_obj, 1, 2, mp_set_global_volume_dB); + +STATIC mp_obj_t mp_count_sources(size_t n_args, const mp_obj_t *args) { + uint16_t d = count_audio_sources(); + return mp_obj_new_int(d); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_count_sources_obj, 0, 2, mp_count_sources); + +STATIC mp_obj_t mp_dump_all_sources(size_t n_args, const mp_obj_t *args) { + uint16_t d = count_audio_sources(); + for(uint16_t i = 0; i < d; i++){ + remove_audio_source(i); + } + return mp_obj_new_int(d); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_dump_all_sources_obj, 0, 2, mp_dump_all_sources); + +STATIC const mp_rom_map_elem_t mp_module_badge_audio_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_badge_audio) }, + { MP_ROM_QSTR(MP_QSTR_set_global_volume_dB), MP_ROM_PTR(&mp_set_global_volume_dB_obj) }, + { MP_ROM_QSTR(MP_QSTR_count_sources), MP_ROM_PTR(&mp_count_sources_obj) }, + { MP_ROM_QSTR(MP_QSTR_dump_all_sources), MP_ROM_PTR(&mp_dump_all_sources_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_badge_audio_globals, mp_module_badge_audio_globals_table); + +const mp_obj_module_t mp_module_badge_audio = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_badge_audio_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_badge_audio, mp_module_badge_audio); + diff --git a/micropython/ports/esp32/badge23_mp_tinysynth.c b/micropython/ports/esp32/badge23_mp_tinysynth.c new file mode 100644 index 0000000000000000000000000000000000000000..a1364c8be21187be658959f922d79dbef22b81b9 --- /dev/null +++ b/micropython/ports/esp32/badge23_mp_tinysynth.c @@ -0,0 +1,101 @@ +#include <stdio.h> +#include "py/runtime.h" +#include "py/obj.h" +#include "../../../badge23/synth.h" +#include "../../../badge23/audio.h" + +typedef struct _synth_tinysynth_obj_t { + mp_obj_base_t base; + trad_osc_t osc; + uint16_t source_index; +} synth_tinysynth_obj_t; + +const mp_obj_type_t synth_tinysynth_type; + +STATIC void tinysynth_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + synth_tinysynth_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_print_str(print, "tinysynth("); + mp_obj_print_helper(print, mp_obj_new_int(self->source_index), PRINT_REPR); + mp_print_str(print, ")"); +} + +STATIC mp_obj_t tinysynth_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 2, 2, true); + synth_tinysynth_obj_t *self = m_new_obj(synth_tinysynth_obj_t); + self->base.type = &synth_tinysynth_type; + self->osc.decay_steps = 50; + self->osc.attack_steps = 3; + self->osc.vol = 1.; + self->osc.gate = 0.01; + self->osc.freq = mp_obj_get_int(args[0]); + self->osc.counter = 0; + self->osc.bend = 1; + self->osc.noise_reg = 1; + self->osc.waveform = 1; + self->osc.skip_hold = mp_obj_get_int(args[1]); + //set_extra_synth(&(self->osc)); + self->source_index = add_audio_source(&(self->osc), trad_osc); + + return MP_OBJ_FROM_PTR(self); +} + +// Class methods +STATIC mp_obj_t tinysynth_start(mp_obj_t self_in) { + synth_tinysynth_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->osc.env_phase = 1; + return mp_obj_new_int(1); +} +MP_DEFINE_CONST_FUN_OBJ_1(tinysynth_start_obj, tinysynth_start); + +STATIC mp_obj_t tinysynth_stop(mp_obj_t self_in) { + synth_tinysynth_obj_t *self = MP_OBJ_TO_PTR(self_in); + if(self->osc.env_phase){ + self->osc.env_phase = 3; + } + return mp_obj_new_int(1); +} +MP_DEFINE_CONST_FUN_OBJ_1(tinysynth_stop_obj, tinysynth_stop); + +STATIC mp_obj_t tinysynth_deinit(mp_obj_t self_in) { + synth_tinysynth_obj_t *self = MP_OBJ_TO_PTR(self_in); + remove_audio_source(self->source_index); + return mp_obj_new_int(1); +} + +MP_DEFINE_CONST_FUN_OBJ_1(tinysynth_deinit_obj, tinysynth_deinit); + +STATIC const mp_rom_map_elem_t tinysynth_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&tinysynth_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&tinysynth_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&tinysynth_deinit_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(tinysynth_locals_dict, tinysynth_locals_dict_table); + + +MP_DEFINE_CONST_OBJ_TYPE( + synth_tinysynth_type, + MP_QSTR_synth, + MP_TYPE_FLAG_NONE, + make_new, tinysynth_make_new, + print, tinysynth_print, + locals_dict, &tinysynth_locals_dict + ); + +STATIC const mp_map_elem_t synth_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_synth) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_tinysynth), (mp_obj_t)&synth_tinysynth_type }, +}; + +STATIC MP_DEFINE_CONST_DICT ( + mp_module_synth_globals, + synth_globals_table +); + +const mp_obj_module_t synth_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_synth_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_synth, synth_user_cmodule); diff --git a/micropython/ports/esp32/main/CMakeLists.txt b/micropython/ports/esp32/main/CMakeLists.txt index 6a7dbc97feeed956f0605d7cf13c9af1e04f696c..d518187e1dd7702489f23f4a3348b27c78f16b40 100644 --- a/micropython/ports/esp32/main/CMakeLists.txt +++ b/micropython/ports/esp32/main/CMakeLists.txt @@ -60,6 +60,8 @@ set(MICROPY_SOURCE_DRIVERS ) set(MICROPY_SOURCE_PORT + ${PROJECT_DIR}/badge23_mp_audio.c + ${PROJECT_DIR}/badge23_mp_tinysynth.c ${PROJECT_DIR}/main.c ${PROJECT_DIR}/uart.c ${PROJECT_DIR}/usb.c