From d3965e03465b2506b3ec9cb9b6646d2b74f64c3a Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Mon, 3 Nov 2025 00:09:10 -0500 Subject: [PATCH 01/17] Support config.write_samples_fn as alternative to built-in I2S. --- examples/AMY_pico_PWM/AMY_pico_PWM.ino | 74 +++++++++++++++++++++ src/amy.h | 14 ++-- src/api.c | 23 +++++++ src/i2s.c | 92 +++++++++++++++----------- src/teensy_support.cpp | 8 ++- 5 files changed, 166 insertions(+), 45 deletions(-) create mode 100644 examples/AMY_pico_PWM/AMY_pico_PWM.ino diff --git a/examples/AMY_pico_PWM/AMY_pico_PWM.ino b/examples/AMY_pico_PWM/AMY_pico_PWM.ino new file mode 100644 index 00000000..62150ce1 --- /dev/null +++ b/examples/AMY_pico_PWM/AMY_pico_PWM.ino @@ -0,0 +1,74 @@ +#include + +// AMY_pico_PWM +// +// Runs AMY using arduino-pico's PWM audio output. +// This version responds to serial MIDI input. +// dpwe 2025-10-26 + +// If you define this, we write to PWM using AMY's write_samples_fn hook +// but leaving it undefined means we pass the sample block in our loop() fn. +//#define USE_AMY_WRITE_SAMPLES_FN + +extern "C" { + extern void example_sequencer_drums_synth(uint32_t start); +} + +#include +PWMAudio pwm(0, true); // PWM Stereo out on pins 0 and 1. + +// Have to introduce this function to work around virtual overloaded write function. +size_t pwm_write(const uint8_t *buffer, size_t nbytes) { + return pwm.write(buffer, nbytes); +} + +void setup() { +#ifdef LED_BUILTIN + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, 1); +#endif + + // Setup PWM + pwm.setBuffers(4, AMY_BLOCK_SIZE * AMY_NCHANS * sizeof(int16_t) / sizeof(int32_t)); + pwm.begin(44100); + + amy_config_t amy_config = amy_default_config(); + amy_config.features.startup_bleep = 1; + amy_config.features.default_synths = 1; + + amy_config.midi = AMY_MIDI_IS_UART; + // Pins for UART MIDI + amy_config.midi_in = 5; + + #ifdef USE_AMY_WRITE_SAMPLES_FN + // Configure to pass samples to PWM. + amy_config.write_samples_fn = pwm_write; + #endif + + amy_start(amy_config); + + // Set a drum loop going, as an example. + example_sequencer_drums_synth(2000); +} + +static long last_millis = 0; +static const long millis_interval = 250; +static bool led_state = 0; + +void loop() { + int16_t *block = amy_update(); +#ifndef USE_AMY_WRITE_SAMPLES_FN + // We have opted to handle our own sample writing. + pwm_write((uint8_t *)block, AMY_BLOCK_SIZE * AMY_NCHANS * sizeof(int16_t)); +#endif + + // Flash on-board LED every 250ms + int now_millis = millis(); + if ((now_millis - last_millis) > millis_interval) { + last_millis = now_millis; + led_state = !led_state; +#ifdef LED_BUILTIN + digitalWrite(LED_BUILTIN, led_state); // turn the LED on (HIGH is the voltage level) +#endif + } +} diff --git a/src/amy.h b/src/amy.h index 3b302701..21f97d10 100644 --- a/src/amy.h +++ b/src/amy.h @@ -593,6 +593,9 @@ typedef struct { uint32_t max_synths; uint32_t max_memory_patches; + // alternative audio output function + size_t (*write_samples_fn)(const uint8_t *buffer, size_t buffer_size); + // pins for MCU platforms int8_t i2s_lrc; int8_t i2s_dout; @@ -748,10 +751,13 @@ void amy_start(amy_config_t); void amy_stop(); void amy_live_start(); void amy_live_stop(); -int16_t * amy_simple_fill_buffer() ; -void amy_update(); -int16_t *amy_render_audio(); -void amy_pass_to_i2s(const int16_t *block); + +int16_t *amy_update(); // in api.c +void amy_hardware_init(); // in i2s.c +void amy_update_tasks(); // in i2s.c +int16_t *amy_render_audio(); // in i2s.c +size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes); // in i2s.c + amy_config_t amy_default_config(); void amy_clear_event(amy_event *e); amy_event amy_default_event(); diff --git a/src/api.c b/src/api.c index fe0ca3dd..67d2f5b9 100644 --- a/src/api.c +++ b/src/api.c @@ -33,6 +33,8 @@ amy_config_t amy_default_config() { c.features.default_synths = 1; c.features.startup_bleep = 0; + c.write_samples_fn = NULL; + c.midi = AMY_MIDI_IS_NONE; #ifndef AMY_MCU c.audio = AMY_AUDIO_IS_MINIAUDIO; @@ -324,4 +326,25 @@ void amy_start(amy_config_t c) { else amy_bleep(0); // bleep using raw oscs. } + amy_hardware_init(); + amy_global.running = 1; + //amy_live_start(); +} + +int16_t *amy_update() { + // Single function to update buffers. + amy_update_tasks(); + int16_t *block = amy_render_audio(); + if (amy_global.config.audio == AMY_AUDIO_IS_I2S + && amy_global.config.i2s_dout != -1) { + amy_i2s_write( + (uint8_t *)block, AMY_BLOCK_SIZE * AMY_NCHANS * sizeof(int16_t) + ); + } + if (amy_global.config.write_samples_fn) { + amy_global.config.write_samples_fn( + (uint8_t *)block, AMY_BLOCK_SIZE * AMY_NCHANS * sizeof(int16_t) + ); + } + return block; } diff --git a/src/i2s.c b/src/i2s.c index a9fd5dd4..dfc75e37 100644 --- a/src/i2s.c +++ b/src/i2s.c @@ -176,7 +176,7 @@ void esp_fill_audio_buffer_task() { xTaskNotifyGive(amy_render_handle); // Render me amy_render(AMY_OSCS/2, AMY_OSCS, 0); - // Wait for the other core to finish + // Wait for the other core to finish ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // Write to i2s @@ -184,39 +184,52 @@ void esp_fill_audio_buffer_task() { AMY_PROFILE_STOP(AMY_ESP_FILL_BUFFER) - size_t written = 0; + if (amy_global.config.i2s_dout != -1) { + size_t written = 0; #ifdef I2S_32BIT // including AMYBOARD - // Convert to 32 bits - for (int i = 0; i < AMY_BLOCK_SIZE * AMY_NCHANS; ++i) - block32[i] = ((int32_t)block[i]) << 16; - i2s_channel_write(tx_handle, block32, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE, &written, portMAX_DELAY); + // Convert to 32 bits + for (int i = 0; i < AMY_BLOCK_SIZE * AMY_NCHANS; ++i) + block32[i] = ((int32_t)block[i]) << 16; + i2s_channel_write(tx_handle, block32, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE, &written, portMAX_DELAY); #else // 16 bit I2S - i2s_channel_write(tx_handle, block, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE, &written, portMAX_DELAY); + i2s_channel_write(tx_handle, block, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE, &written, portMAX_DELAY); #endif - if(written != AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE) { - fprintf(stderr,"i2s output underrun: %d vs %d\n", written, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE); + if(written != AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE) { + fprintf(stderr,"i2s output underrun: %d vs %d\n", written, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE); + } } } } // init AMY from the esp. wraps some amy funcs in a task to do multicore rendering on the ESP32 -amy_err_t i2s_amy_init() { +void amy_hardware_init() { // Start i2s - esp32_setup_i2s(); + if (amy_global.config.i2s_dout != -1) { + esp32_setup_i2s(); + } // Create the second core rendering task xTaskCreatePinnedToCore(&esp_render_task, AMY_RENDER_TASK_NAME, AMY_RENDER_TASK_STACK_SIZE, NULL, AMY_RENDER_TASK_PRIORITY, &amy_render_handle, AMY_RENDER_TASK_COREID); // And the fill audio buffer thread, combines, does volume & filters xTaskCreatePinnedToCore(&esp_fill_audio_buffer_task, AMY_FILL_BUFFER_TASK_NAME, AMY_FILL_BUFFER_TASK_STACK_SIZE, NULL, AMY_FILL_BUFFER_TASK_PRIORITY, &amy_fill_buffer_handle, AMY_FILL_BUFFER_TASK_COREID); - return AMY_OK; } -void amy_update() { +void amy_update_tasks() { + // does nothing on esp +} + +int16_t *amy_render_audio() { + // does nothing on esp + return NULL; +} + +size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes) { // does nothing on esp + return 1; } #elif (defined ARDUINO_ARCH_RP2040) || (defined ARDUINO_ARCH_RP2350) @@ -229,6 +242,10 @@ void amy_update() { #include "pico/binary_info.h" #include "pico/util/queue.h" +// Provided by pico_support.cpp +extern void pico_setup_i2s(amy_config_t *config); +extern void pico_i2s_read_write_buffer(int16_t *in_samples, const int16_t *out_samples, int nframes); + struct audio_buffer_pool *ap; static inline uint32_t _millis(void) @@ -254,7 +271,7 @@ int32_t render_other_core(int32_t data) { extern void on_pico_uart_rx(); -void amy_poll_tasks() { +void amy_update_tasks() { //if (ap->free_list == NULL) { amy_execute_deltas(); if(amy_global.config.midi & AMY_MIDI_IS_UART) on_pico_uart_rx(); @@ -280,11 +297,11 @@ int16_t *amy_render_audio() { return block; } -void amy_update() { +size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes) { // Single function to update buffers. - amy_poll_tasks(); - int16_t *block = amy_render_audio(); - amy_pass_to_i2s(block); + // len is the number of int16 sample frames. + pico_i2s_read_write_buffer(amy_in_block, (const int16_t *)buffer, AMY_BLOCK_SIZE); + return nbytes; } @@ -332,17 +349,10 @@ void core1_main() { } } -// Provided by pico_support.cpp -extern void pico_setup_i2s(amy_config_t *config); -extern void pico_i2s_read_write_buffer(int16_t *in_samples, const int16_t *out_samples, int nframes); - -void amy_pass_to_i2s(const int16_t *block) { - // len is the number of int16 sample frames. - pico_i2s_read_write_buffer(amy_in_block, block, AMY_BLOCK_SIZE); -} - -amy_err_t i2s_amy_init() { - pico_setup_i2s(&amy_global.config); +void amy_hardware_init() { + if (amy_global.config.i2s_dout != -1) { + pico_setup_i2s(&amy_global.config); + } #ifdef USE_SECOND_CORE queue_init(&call_queue, sizeof(queue_entry_t), 2); @@ -351,7 +361,6 @@ amy_err_t i2s_amy_init() { multicore_launch_core1_with_stack(core1_main, core1_separate_stack_address, 0x2000); sleep_ms(500); #endif - return AMY_OK; } @@ -359,16 +368,17 @@ amy_err_t i2s_amy_init() { extern void teensy_setup_i2s(); -extern void teensy_i2s_send(int16_t *samples); +extern size_t teensy_i2s_write(const uint8_t *buffer, size_t nbytes); extern int16_t teensy_get_serial_byte(); -amy_err_t i2s_amy_init() { - teensy_setup_i2s(); - return AMY_OK; +void amy_hardware_init() { + if (amy_global.config.i2s_dout != -1) { + teensy_setup_i2s(); + } } -void amy_update() { +void amy_update_tasks() { if(amy_config.global.midi & AMY_MIDI_IS_UART) { // do midi in here uint8_t bytes[1]; @@ -379,9 +389,16 @@ void amy_update() { } } amy_execute_deltas(); +} + +int16_t *amy_render_audio() { amy_render(0, AMY_OSCS, 0); int16_t *block = amy_fill_buffer(); - teensy_i2s_send(block); + return block; +} + +size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes) { + return teensy_i2s_send(buffer, nbytes); } #else @@ -391,10 +408,9 @@ void amy_update() { #endif - void amy_live_start() { amy_global.running = 1; - i2s_amy_init(); + amy_hardware_init(); } diff --git a/src/teensy_support.cpp b/src/teensy_support.cpp index 64558550..7771c657 100644 --- a/src/teensy_support.cpp +++ b/src/teensy_support.cpp @@ -54,13 +54,15 @@ extern "C" { } - void teensy_i2s_send(int16_t * samples) { - for(int16_t i=0;i Date: Mon, 3 Nov 2025 00:12:53 -0500 Subject: [PATCH 02/17] Try to avoid issues with calling amy_live_start() twice. --- examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino | 1 - src/i2s.c | 9 ++++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino b/examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino index bef12f8c..819160be 100644 --- a/examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino +++ b/examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino @@ -96,7 +96,6 @@ void setup() { amy_config.midi_in = 5; amy_start(amy_config); - amy_live_start(); //test_polyphony(); //test_sequencer(); diff --git a/src/i2s.c b/src/i2s.c index dfc75e37..68d2fae9 100644 --- a/src/i2s.c +++ b/src/i2s.c @@ -409,14 +409,17 @@ size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes) { void amy_live_start() { - amy_global.running = 1; - amy_hardware_init(); + // Try to avoid doing this more than once. + if (!amy_global.running) { + amy_hardware_init(); + amy_global.running = 1; + } } void amy_live_stop() { amy_global.running = 0; - // Not sure we do anythign on mcus for stopping amy live + // Not sure we do anything on mcus for stopping amy live } From 9e8e9f4dfe69397c80922e0cef4a47989b98bea2 Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Fri, 28 Nov 2025 18:25:09 -0500 Subject: [PATCH 03/17] amy.h: start of hardware bitmask in struct amy_config. --- src/amy.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/amy.h b/src/amy.h index 21f97d10..9f0b23ff 100644 --- a/src/amy.h +++ b/src/amy.h @@ -570,6 +570,13 @@ typedef struct delay_line { #define AMY_MIDI_IS_MACOS 0x04 #define AMY_MIDI_IS_WEBMIDI 0x08 +// Values for config.hardware +#define AMY_HARDWARE_NONE 0x00 +// Use secondary cores if available. +#define AMY_HARDWARE_MULTICORE 0x01 +// Use multitasking (e.g. RTOS threads) if available. +#define AMY_HARDWARE_MULTITHREAD 0x01 + typedef struct { struct { @@ -582,8 +589,9 @@ typedef struct { uint8_t custom : 1; uint8_t startup_bleep : 1; } features; - uint8_t midi; + uint8_t midi; uint8_t audio; + uint32_t hardware; // variables uint16_t max_oscs; From 29dc11908265ea363691c75f9502019838ec213f Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Sun, 30 Nov 2025 23:18:17 -0500 Subject: [PATCH 04/17] Arduino for RPi Pico works; miniaudio broken (so macbook targets give core dump). --- amy/constants.py | 3 + src/amy-example.c | 3 - src/amy.h | 10 +-- src/amy_midi.c | 25 ++++--- src/api.c | 18 +++-- src/i2s.c | 140 +++++++++++++++++++++------------------ src/libminiaudio-audio.c | 42 +++++++++--- src/pico_support.cpp | 4 +- src/pyamy.c | 3 +- 9 files changed, 146 insertions(+), 102 deletions(-) diff --git a/amy/constants.py b/amy/constants.py index 06cb0fb4..fb33afd0 100644 --- a/amy/constants.py +++ b/amy/constants.py @@ -111,3 +111,6 @@ AMY_MIDI_IS_USB_GADGET=0x02 AMY_MIDI_IS_MACOS=0x04 AMY_MIDI_IS_WEBMIDI=0x08 +AMY_HARDWARE_NONE=0x00 +AMY_HARDWARE_MULTICORE=0x01 +AMY_HARDWARE_MULTITHREAD=0x02 diff --git a/src/amy-example.c b/src/amy-example.c index bf903ea8..95b69f77 100644 --- a/src/amy-example.c +++ b/src/amy-example.c @@ -63,7 +63,6 @@ int main(int argc, char ** argv) { amy_config.features.default_synths = 0; amy_start(amy_config); - amy_live_start(); //example_fm(0); //example_voice_chord(0,0); example_synth_chord(0, /* patch */ 0); @@ -86,8 +85,6 @@ int main(int argc, char ** argv) { //show_debug(99); - amy_live_stop(); - amy_stop(); return 0; diff --git a/src/amy.h b/src/amy.h index 9f0b23ff..f6934fda 100644 --- a/src/amy.h +++ b/src/amy.h @@ -79,7 +79,8 @@ extern const uint16_t pcm_samples; #define AMY_HAS_STARTUP_BLEEP (amy_global.config.features.startup_bleep) #define AMY_HAS_REVERB (amy_global.config.features.reverb) -#define AMY_HAS_AUDIO_IN (amy_global.config.features.audio_in) +#define AMY_HAS_AUDIO_IN (amy_global.config.i2s_din != -1) +#define AMY_HAS_I2S (amy_global.config.i2s_dout != -1) #define AMY_HAS_DEFAULT_SYNTHS (amy_global.config.features.default_synths) #define AMY_HAS_CHORUS (amy_global.config.features.chorus) #define AMY_HAS_ECHO (amy_global.config.features.echo) @@ -575,7 +576,7 @@ typedef struct delay_line { // Use secondary cores if available. #define AMY_HARDWARE_MULTICORE 0x01 // Use multitasking (e.g. RTOS threads) if available. -#define AMY_HARDWARE_MULTITHREAD 0x01 +#define AMY_HARDWARE_MULTITHREAD 0x02 typedef struct { @@ -583,7 +584,6 @@ typedef struct { uint8_t chorus : 1; uint8_t reverb : 1; uint8_t echo : 1; - uint8_t audio_in : 1; uint8_t default_synths : 1; uint8_t partials : 1; uint8_t custom : 1; @@ -606,8 +606,8 @@ typedef struct { // pins for MCU platforms int8_t i2s_lrc; - int8_t i2s_dout; - int8_t i2s_din; + int8_t i2s_dout; // -1 == no I2S + int8_t i2s_din; // -1 == no AUDIO_IN int8_t i2s_bclk; int8_t i2s_mclk; uint16_t i2s_mclk_mult; diff --git a/src/amy_midi.c b/src/amy_midi.c index a484a679..764673a2 100644 --- a/src/amy_midi.c +++ b/src/amy_midi.c @@ -322,8 +322,7 @@ int8_t esp_get_uart(int8_t index) { return -1; } -void run_midi_task() { - const int uart_num = esp_get_uart(amy_global.config.midi_uart); +void esp_init_midi(void) { uart_config_t uart_config = { .baud_rate = 31250, .data_bits = UART_DATA_8_BITS, @@ -352,21 +351,31 @@ void run_midi_task() { }; uart_intr_config(uart_num, &uart_intr); +} +void esp_poll_midi(void) { + const int uart_num = esp_get_uart(amy_global.config.midi_uart); uint8_t data[MAX_MIDI_BYTES_TO_PARSE]; - size_t length = 0; + int length = uart_read_bytes(uart_num, data, MAX_MIDI_BYTES_TO_PARSE /*MAX_MIDI_BYTES_PER_MESSAGE*MIDI_QUEUE_DEPTH*/, 1/portTICK_PERIOD_MS); + if(length > 0) { + convert_midi_bytes_to_messages(data,length,0); + } +} + +void run_midi_task() { + while(1) { - length = uart_read_bytes(uart_num, data, MAX_MIDI_BYTES_TO_PARSE /*MAX_MIDI_BYTES_PER_MESSAGE*MIDI_QUEUE_DEPTH*/, 1/portTICK_PERIOD_MS); - if(length > 0) { - convert_midi_bytes_to_messages(data,length,0); - } + esp_poll_midi(); } // end loop forever } void run_midi() { sysex_buffer = malloc_caps(MAX_SYSEX_BYTES, amy_global.config.ram_caps_sysex); if(amy_global.config.midi & AMY_MIDI_IS_UART) { - xTaskCreatePinnedToCore(run_midi_task, MIDI_TASK_NAME, (MIDI_TASK_STACK_SIZE) / sizeof(StackType_t), NULL, MIDI_TASK_PRIORITY, &midi_handle, MIDI_TASK_COREID); + esp_init_midi(); + if (amy_global.config.hardware & AMY_HARDWARE_MULTITHREAD) { + xTaskCreatePinnedToCore(run_midi_task, MIDI_TASK_NAME, (MIDI_TASK_STACK_SIZE) / sizeof(StackType_t), NULL, MIDI_TASK_PRIORITY, &midi_handle, MIDI_TASK_COREID); + } // otherwise esp_poll_midi is called from amy_update_tasks() } } diff --git a/src/api.c b/src/api.c index 67d2f5b9..38e6f9a3 100644 --- a/src/api.c +++ b/src/api.c @@ -29,7 +29,6 @@ amy_config_t amy_default_config() { c.features.chorus = 1; c.features.partials = 1; c.features.custom = 1; - c.features.audio_in = 1; c.features.default_synths = 1; c.features.startup_bleep = 0; @@ -72,8 +71,8 @@ amy_config_t amy_default_config() { c.playback_device_id = -1; c.i2s_lrc = -1; - c.i2s_dout = -1; - c.i2s_din = -1; + c.i2s_dout = -1; // -1 implies no I2S + c.i2s_din = -1; // -1 implies no AUDIO_IN c.i2s_bclk = -1; c.i2s_mclk = -1; c.i2s_mclk_mult = 256; // MCLK = mclk_mult * Fs. @@ -313,9 +312,10 @@ void amy_bleep_synth(uint32_t start) { amy_add_event(&e); } +extern void *miniaudio_run(void *vargp); + void amy_start(amy_config_t c) { global_init(c); - run_midi(); amy_profiles_init(); sequencer_start(); oscs_init(); @@ -327,16 +327,20 @@ void amy_start(amy_config_t c) { amy_bleep(0); // bleep using raw oscs. } amy_hardware_init(); + run_midi(); // Must be after hardware_init in case F_CPU is modified on RP2040 Arduino. +#if !defined(ESP_PLATFORM) && !defined(PICO_ON_DEVICE) && !defined(ARDUINO) + // This is a WASM/libminiaudio setup, we still need live_start. + pthread_t amy_live_thread; + pthread_create(&amy_live_thread, NULL, miniaudio_run, NULL); +#endif amy_global.running = 1; - //amy_live_start(); } int16_t *amy_update() { // Single function to update buffers. amy_update_tasks(); int16_t *block = amy_render_audio(); - if (amy_global.config.audio == AMY_AUDIO_IS_I2S - && amy_global.config.i2s_dout != -1) { + if (AMY_HAS_I2S) { amy_i2s_write( (uint8_t *)block, AMY_BLOCK_SIZE * AMY_NCHANS * sizeof(int16_t) ); diff --git a/src/i2s.c b/src/i2s.c index 68d2fae9..d71bb283 100644 --- a/src/i2s.c +++ b/src/i2s.c @@ -153,6 +153,10 @@ void esp_render_task( void * pvParameters) { #endif extern output_sample_type * amy_in_block; +// Place where render thread leaves address of samples. +// Set by esp_fill_audio_buffer_task, cleared when returned by amy_render_audio (if used). +int16_t *volatile last_audio_buffer = NULL; + // Make AMY's FABT run forever , as a FreeRTOS task void esp_fill_audio_buffer_task() { while(1) { @@ -184,51 +188,71 @@ void esp_fill_audio_buffer_task() { AMY_PROFILE_STOP(AMY_ESP_FILL_BUFFER) - if (amy_global.config.i2s_dout != -1) { - size_t written = 0; - -#ifdef I2S_32BIT // including AMYBOARD - // Convert to 32 bits - for (int i = 0; i < AMY_BLOCK_SIZE * AMY_NCHANS; ++i) - block32[i] = ((int32_t)block[i]) << 16; - i2s_channel_write(tx_handle, block32, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE, &written, portMAX_DELAY); -#else // 16 bit I2S - i2s_channel_write(tx_handle, block, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE, &written, portMAX_DELAY); - -#endif - - if(written != AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE) { - fprintf(stderr,"i2s output underrun: %d vs %d\n", written, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE); - } + if (AMY_HAS_I2S) { + amy_i2s_write((uint8_t *)block, AMY_BLOCK_SIZE * AMY_NCHANS * sizeof(int16_t)); } + last_audio_buffer = block; } } // init AMY from the esp. wraps some amy funcs in a task to do multicore rendering on the ESP32 void amy_hardware_init() { // Start i2s - if (amy_global.config.i2s_dout != -1) { + if (AMY_HAS_I2S) { esp32_setup_i2s(); } - - // Create the second core rendering task - xTaskCreatePinnedToCore(&esp_render_task, AMY_RENDER_TASK_NAME, AMY_RENDER_TASK_STACK_SIZE, NULL, AMY_RENDER_TASK_PRIORITY, &amy_render_handle, AMY_RENDER_TASK_COREID); - - // And the fill audio buffer thread, combines, does volume & filters - xTaskCreatePinnedToCore(&esp_fill_audio_buffer_task, AMY_FILL_BUFFER_TASK_NAME, AMY_FILL_BUFFER_TASK_STACK_SIZE, NULL, AMY_FILL_BUFFER_TASK_PRIORITY, &amy_fill_buffer_handle, AMY_FILL_BUFFER_TASK_COREID); + if (amy_global.config.hardware & AMY_HARDWARE_MULTITHREAD) { + // Create the second core rendering task + xTaskCreatePinnedToCore(&esp_render_task, AMY_RENDER_TASK_NAME, AMY_RENDER_TASK_STACK_SIZE, NULL, AMY_RENDER_TASK_PRIORITY, &amy_render_handle, AMY_RENDER_TASK_COREID); + // And the fill audio buffer thread, combines, does volume & filters + xTaskCreatePinnedToCore(&esp_fill_audio_buffer_task, AMY_FILL_BUFFER_TASK_NAME, AMY_FILL_BUFFER_TASK_STACK_SIZE, NULL, AMY_FILL_BUFFER_TASK_PRIORITY, &amy_fill_buffer_handle, AMY_FILL_BUFFER_TASK_COREID); + } } +extern void esp_poll_midi(void); + void amy_update_tasks() { - // does nothing on esp + if ((amy_global.config.hardware & AMY_HARDWARE_MULTITHREAD) == 0) { + amy_execute_deltas(); + esp_poll_midi(); + } else{ + // Rendering is happening on separate thread, nothing to do. + } } int16_t *amy_render_audio() { - // does nothing on esp - return NULL; + int16_t *buf = NULL; + if ((amy_global.config.hardware & AMY_HARDWARE_MULTITHREAD) == 0) { + amy_render(0, AMY_OSCS, 0); + buf = amy_fill_buffer(); + } else { + // Wait for esp_fill_audio_buffer_task thread to give us something. + while( (buf = last_audio_buffer) == NULL) + ; + // Clear the last_audio_buffer semaphore so we will wait for the next render next time. + last_audio_buffer = NULL; + } + return buf; } size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes) { // does nothing on esp + size_t written = 0; + +#ifdef I2S_32BIT // including AMYBOARD + // Convert to 32 bits + for (int i = 0; i < AMY_BLOCK_SIZE * AMY_NCHANS; ++i) + block32[i] = ((int32_t)block[i]) << 16; + i2s_channel_write(tx_handle, block32, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE, &written, portMAX_DELAY); +#else // 16 bit I2S + i2s_channel_write(tx_handle, block, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE, &written, portMAX_DELAY); + +#endif + + if(written != AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE) { + fprintf(stderr,"i2s output underrun: %d vs %d\n", written, AMY_BLOCK_SIZE * AMY_NCHANS * I2S_BYTES_PER_SAMPLE); + } + } return 1; } @@ -263,36 +287,34 @@ queue_t call_queue; queue_t results_queue; -int32_t render_other_core(int32_t data) { - amy_render(AMY_OSCS/2, AMY_OSCS, 1); - return AMY_OK; -} - extern void on_pico_uart_rx(); - void amy_update_tasks() { - //if (ap->free_list == NULL) { amy_execute_deltas(); if(amy_global.config.midi & AMY_MIDI_IS_UART) on_pico_uart_rx(); #ifdef TUD_USB_GADGET if(amy_global.config.midi & AMY_MIDI_IS_USB_GADGET) on_pico_uart_rx(); #endif - //} } -int16_t *amy_render_audio() { - //if (ap->free_list != NULL) { #define USE_SECOND_CORE + +int32_t render_other_core(int32_t data) { + amy_render(AMY_OSCS/2, AMY_OSCS, 1); + return AMY_OK; +} + +int16_t *amy_render_audio() { #ifdef USE_SECOND_CORE - int32_t res; - queue_entry_t entry = {render_other_core, AMY_OK}; - queue_add_blocking(&call_queue, &entry); - amy_render(0, AMY_OSCS/2, 0); - queue_remove_blocking(&results_queue, &res); -#else - amy_render(0, AMY_OSCS, 0); + if (amy_global.config.hardware & AMY_HARDWARE_MULTICORE) { + int32_t res; + queue_entry_t entry = {render_other_core, AMY_OK}; + queue_add_blocking(&call_queue, &entry); + amy_render(0, AMY_OSCS/2, 0); + queue_remove_blocking(&results_queue, &res); + } else #endif + amy_render(0, AMY_OSCS, 0); int16_t *block = amy_fill_buffer(); return block; } @@ -350,16 +372,17 @@ void core1_main() { } void amy_hardware_init() { - if (amy_global.config.i2s_dout != -1) { + if (AMY_HAS_I2S) { pico_setup_i2s(&amy_global.config); } - #ifdef USE_SECOND_CORE - queue_init(&call_queue, sizeof(queue_entry_t), 2); - queue_init(&results_queue, sizeof(int32_t), 2); - uint32_t * core1_separate_stack_address = (uint32_t*)malloc(0x2000); - multicore_launch_core1_with_stack(core1_main, core1_separate_stack_address, 0x2000); - sleep_ms(500); + if (amy_global.config.hardware & AMY_HARDWARE_MULTICORE) { + queue_init(&call_queue, sizeof(queue_entry_t), 2); + queue_init(&results_queue, sizeof(int32_t), 2); + uint32_t * core1_separate_stack_address = (uint32_t*)malloc(0x2000); + multicore_launch_core1_with_stack(core1_main, core1_separate_stack_address, 0x2000); + sleep_ms(500); + } #endif } @@ -373,7 +396,7 @@ extern size_t teensy_i2s_write(const uint8_t *buffer, size_t nbytes); extern int16_t teensy_get_serial_byte(); void amy_hardware_init() { - if (amy_global.config.i2s_dout != -1) { + if (AMY_HAS_I2S) { teensy_setup_i2s(); } } @@ -408,19 +431,4 @@ size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes) { #endif -void amy_live_start() { - // Try to avoid doing this more than once. - if (!amy_global.running) { - amy_hardware_init(); - amy_global.running = 1; - } -} - - -void amy_live_stop() { - amy_global.running = 0; - // Not sure we do anything on mcus for stopping amy live -} - - #endif diff --git a/src/libminiaudio-audio.c b/src/libminiaudio-audio.c index 66c12207..3d8a0b06 100644 --- a/src/libminiaudio-audio.c +++ b/src/libminiaudio-audio.c @@ -49,8 +49,27 @@ void main_loop__em() } #endif -void amy_update() { - // does nothing on miniaudio +// Semaphore for passing most recent audio buffer to amy_update. +// It's the pointer that's volatile, not the data it points to. +int16_t *volatile last_audio_buffer = NULL; + +void amy_update_tasks() { +} + +void amy_hardware_init() { +} + +size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes) { + return 0; +} + +int16_t *amy_render_audio() { + // For miniaudio, we just return a semaphore buffer. + while (last_audio_buffer == NULL) + ; + int16_t *buf = last_audio_buffer; + last_audio_buffer = NULL; + return buf; } void amy_print_devices() { @@ -104,7 +123,9 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, } if(in_ptr == (AMY_BLOCK_SIZE*AMY_NCHANS)) { // we have a block of input ready // render and copy into output ring buffer - int16_t * buf = amy_simple_fill_buffer(); + int16_t * buf = amy_fill_buffer(); + // Maybe pass to amy_update. + last_audio_buffer = buf; // reset the input pointer for future input data in_ptr = 0; // copy this output to a ring buffer @@ -218,23 +239,24 @@ void *miniaudio_run(void *vargp) { #ifdef __EMSCRIPTEN__ void amy_live_start_web_audioin() { - amy_global.config.features.audio_in = 1; + //amy_global.config.features.audio_in = 1; + amy_global.config.i2s_din = 0; // Fake, this is how we indicate we have AUDIO_IN emscripten_cancel_main_loop(); miniaudio_init(); emscripten_set_main_loop(main_loop__em, 0, 0); } void amy_live_start_web() { - amy_global.config.features.audio_in = 0; + amy_global.config.i2s_din = -1; // This is how we indicate no AUDIO_IN emscripten_cancel_main_loop(); miniaudio_init(); emscripten_set_main_loop(main_loop__em, 0, 0); } #endif -void amy_live_start() { - // kick off a thread running miniaudio_run - amy_global.running = 1; - pthread_create(&amy_live_thread, NULL, miniaudio_run, NULL); -} +//void amy_live_start() { +// // kick off a thread running miniaudio_run +// amy_global.running = 1; +// pthread_create(&amy_live_thread, NULL, miniaudio_run, NULL); +//} void amy_live_stop() { diff --git a/src/pico_support.cpp b/src/pico_support.cpp index d9078857..14e0a369 100644 --- a/src/pico_support.cpp +++ b/src/pico_support.cpp @@ -62,8 +62,8 @@ extern "C" { #define PLL_PD1 4 #define PLL_PD2 2 #elif F_CPU == 150000000 -// bootup tone works, MIDI doesn't work. LRCK is 44.1 kHz. MCLK is sync'd - #warning "MIDI doesn't work for F_CPU == 150 MHz. Use 133 or 176 instead." +// works as long as CPU clock is modified *before* MIDI UART is initialized. LRCK is 44.1 kHz. MCLK is sync'd +// If MIDI UART is initialized *before* sys_set_clock_pll in pico_setup_i2s, baud rates are wrong and MIDI doesn't work. #define F_CPU_MOD_KHZ 158000 #define F_SAMP 44084 #define VCO_FREQ 948000000 diff --git a/src/pyamy.c b/src/pyamy.c index ff409fa6..951ae2cd 100644 --- a/src/pyamy.c +++ b/src/pyamy.c @@ -66,7 +66,8 @@ static PyObject * config_wrapper(PyObject *self, PyObject *args) { } static PyObject * render_wrapper(PyObject *self, PyObject *args) { - int16_t * result = amy_simple_fill_buffer(); + //int16_t * result = amy_simple_fill_buffer(); + int16_t * result = amy_update(); // Create a python list of ints (they are signed shorts that come back) uint16_t bs = AMY_BLOCK_SIZE; if(AMY_NCHANS == 2) { From f53f57ae82b622ae8c1dc6cd143d643e403ca4f8 Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Mon, 1 Dec 2025 08:19:21 -0500 Subject: [PATCH 05/17] Trying to fix libminiaudio so amy-example will run. --- Makefile | 2 +- src/amy-example.c | 1 + src/amy-message.c | 2 +- src/libminiaudio-audio.c | 8 +++++--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index e638b873..976c919a 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ SOURCES += src/algorithms.c src/amy.c src/envelope.c src/examples.c src/parse.c src/libminiaudio-audio.c src/instrument.c src/amy_midi.c src/api.c src/midi_mappings.c OBJECTS = $(patsubst %.c, %.o, $(SOURCES)) - + HEADERS = $(wildcard src/*.h) HEADERS_BUILD := $(filter-out src/patches.h,$(HEADERS)) diff --git a/src/amy-example.c b/src/amy-example.c index 95b69f77..9ddeec7d 100644 --- a/src/amy-example.c +++ b/src/amy-example.c @@ -60,6 +60,7 @@ int main(int argc, char ** argv) { amy_config.audio = AMY_AUDIO_IS_MINIAUDIO; amy_config.playback_device_id = playback_device_id; amy_config.capture_device_id = capture_device_id; + amy_config.i2s_din = 0; // fake, to indicate has_audio_in amy_config.features.default_synths = 0; amy_start(amy_config); diff --git a/src/amy-message.c b/src/amy-message.c index 38801cf6..a7ed74f3 100644 --- a/src/amy-message.c +++ b/src/amy-message.c @@ -49,7 +49,7 @@ int main(int argc, char ** argv) { amy_config.playback_device_id = playback_device_id; amy_config.capture_device_id = capture_device_id; amy_start(amy_config); - amy_live_start(); + //amy_live_start(); while (1) { char input[1024]; diff --git a/src/libminiaudio-audio.c b/src/libminiaudio-audio.c index 3d8a0b06..3b891ef5 100644 --- a/src/libminiaudio-audio.c +++ b/src/libminiaudio-audio.c @@ -29,7 +29,7 @@ extern SAMPLE ** fbl; int16_t * leftover_buf; uint16_t leftover_samples = 0; -pthread_t amy_live_thread; +//pthread_t amy_live_thread; #ifdef __EMSCRIPTEN__ #include @@ -118,8 +118,10 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, short int *peek = (short *)pInput; // We lag a AMY block behind input here because our frame size is never a multiple of AMY block size for(uint16_t frame=0;frame Date: Mon, 1 Dec 2025 08:43:18 -0500 Subject: [PATCH 06/17] amy-example works again; simple_fill_buffer restored for now; pyamy crashes because of removal of amy_live_start. --- src/amy-example.c | 1 + src/amy.h | 1 + src/libminiaudio-audio.c | 6 +++--- src/pyamy.c | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/amy-example.c b/src/amy-example.c index 9ddeec7d..465957d5 100644 --- a/src/amy-example.c +++ b/src/amy-example.c @@ -61,6 +61,7 @@ int main(int argc, char ** argv) { amy_config.playback_device_id = playback_device_id; amy_config.capture_device_id = capture_device_id; amy_config.i2s_din = 0; // fake, to indicate has_audio_in + amy_config.i2s_dout = 0; // fake, to indicate has_audio_out amy_config.features.default_synths = 0; amy_start(amy_config); diff --git a/src/amy.h b/src/amy.h index f6934fda..7eee2e81 100644 --- a/src/amy.h +++ b/src/amy.h @@ -747,6 +747,7 @@ void hold_and_modify(uint16_t osc) ; void amy_execute_delta(); void amy_execute_deltas(); int16_t * amy_fill_buffer(); +int16_t * amy_simple_fill_buffer(); // excute_deltas + render + fill_buffer uint32_t ms_to_samples(uint32_t ms) ; diff --git a/src/libminiaudio-audio.c b/src/libminiaudio-audio.c index 3b891ef5..fabbb788 100644 --- a/src/libminiaudio-audio.c +++ b/src/libminiaudio-audio.c @@ -125,7 +125,7 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, } if(in_ptr == (AMY_BLOCK_SIZE*AMY_NCHANS)) { // we have a block of input ready // render and copy into output ring buffer - int16_t * buf = amy_fill_buffer(); + int16_t * buf = amy_simple_fill_buffer(); // Maybe pass to amy_update. last_audio_buffer = buf; // reset the input pointer for future input data @@ -213,7 +213,7 @@ amy_err_t miniaudio_init() { deviceConfig.pUserData = _custom; // Force miniaudio's callback to be the same size as AMY. This helps us not loop too many fill_buffer calls - deviceConfig.periodSizeInFrames=AMY_BLOCK_SIZE; + deviceConfig.periodSizeInFrames = AMY_BLOCK_SIZE; if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) { printf("Failed to open playback device.\n"); @@ -226,7 +226,7 @@ amy_err_t miniaudio_init() { exit(1); } for(uint16_t i=0;i Date: Fri, 5 Dec 2025 16:19:42 -0500 Subject: [PATCH 07/17] Works against tulip/get_output_buffer (for amyboard). --- src/amy.c | 5 +++++ src/amy.h | 4 +++- src/amy_midi.c | 1 + src/api.c | 20 ++++++++++++++++---- src/i2s.c | 25 ++++++++++++------------- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/amy.c b/src/amy.c index c08fdb31..4c87fc41 100644 --- a/src/amy.c +++ b/src/amy.c @@ -156,6 +156,9 @@ SAMPLE ** fbl; SAMPLE ** per_osc_fb; SAMPLE core_max[AMY_MAX_CORES]; +// Public pointer to recently-emitted waveform block. No sync for the moment. Cleared to NULL when read by amy_get_output_buffer. +output_sample_type * amy_out_block; +output_sample_type * amy_out_block_copy; // Audio input blocks. Filled by the audio implementation before rendering. // For live audio input from a codec, AUDIO_IN0 / 1 output_sample_type * amy_in_block; @@ -838,6 +841,7 @@ int8_t oscs_init() { bzero(synth, sizeof(struct synthinfo *) * (AMY_OSCS+1)); msynth = (struct mod_synthinfo **) malloc_caps(sizeof(struct mod_synthinfo *) * (AMY_OSCS+1), amy_global.config.ram_caps_synth); block = (output_sample_type *) malloc_caps(sizeof(output_sample_type) * AMY_BLOCK_SIZE * AMY_NCHANS, amy_global.config.ram_caps_block); + amy_out_block_copy = (output_sample_type *) malloc_caps(sizeof(output_sample_type) * AMY_BLOCK_SIZE * AMY_NCHANS, amy_global.config.ram_caps_block); amy_in_block = (output_sample_type*)malloc_caps(sizeof(output_sample_type)*AMY_BLOCK_SIZE*AMY_NCHANS, amy_global.config.ram_caps_block); amy_external_in_block = (output_sample_type*)malloc_caps(sizeof(output_sample_type)*AMY_BLOCK_SIZE*AMY_NCHANS, amy_global.config.ram_caps_block); // set all oscillators to their default values @@ -1723,6 +1727,7 @@ int16_t * amy_fill_buffer() { amy_global.total_blocks++; AMY_PROFILE_STOP(AMY_FILL_BUFFER) + amy_out_block = block; return block; } diff --git a/src/amy.h b/src/amy.h index 7eee2e81..ec429b4f 100644 --- a/src/amy.h +++ b/src/amy.h @@ -705,6 +705,7 @@ extern struct synthinfo** synth; extern struct mod_synthinfo** msynth; extern struct state amy_global; +extern output_sample_type * amy_out_block; extern output_sample_type * amy_in_block; extern output_sample_type * amy_external_in_block; @@ -771,7 +772,8 @@ amy_config_t amy_default_config(); void amy_clear_event(amy_event *e); amy_event amy_default_event(); uint32_t amy_sysclock(); -void amy_get_input_buffer(output_sample_type * samples); +int amy_get_output_buffer(output_sample_type * samples); +int amy_get_input_buffer(output_sample_type * samples); void amy_set_external_input_buffer(output_sample_type * samples); extern uint8_t (*amy_external_render_hook)(uint16_t, SAMPLE*, uint16_t); extern float (*amy_external_coef_hook)(uint16_t); diff --git a/src/amy_midi.c b/src/amy_midi.c index 764673a2..e10a2a3d 100644 --- a/src/amy_midi.c +++ b/src/amy_midi.c @@ -331,6 +331,7 @@ void esp_init_midi(void) { }; // Configure UART parameters + const int uart_num = esp_get_uart(amy_global.config.midi_uart); ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config)); ESP_ERROR_CHECK(uart_set_pin(uart_num, amy_global.config.midi_out, amy_global.config.midi_in, UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE )); diff --git a/src/api.c b/src/api.c index 38e6f9a3..730dfccb 100644 --- a/src/api.c +++ b/src/api.c @@ -32,6 +32,9 @@ amy_config_t amy_default_config() { c.features.default_synths = 1; c.features.startup_bleep = 0; + // Use all hardware features by default. + c.hardware = AMY_HARDWARE_MULTICORE | AMY_HARDWARE_MULTITHREAD; + c.write_samples_fn = NULL; c.midi = AMY_MIDI_IS_NONE; @@ -158,9 +161,18 @@ void amy_clear_event(amy_event *e) { } -// get AUDIO_IN0 and AUDIO_IN1 -void amy_get_input_buffer(output_sample_type * samples) { +// get last-written output, returns number of bytes written. +int amy_get_output_buffer(output_sample_type * samples) { + if (amy_out_block == NULL) return 0; + for(uint16_t i=0;i Date: Sun, 7 Dec 2025 10:50:46 -0500 Subject: [PATCH 08/17] Remove amy_live_start/stop; amy-example and python tests still run. --- amy/__init__.py | 2 +- src/amy-message.c | 3 ++- src/amy-piano.c | 10 +++++++--- src/api.c | 23 ++++++++++++----------- src/libminiaudio-audio.c | 32 +++++++++++++++++++++++++------- src/pyamy.c | 12 ++++++------ 6 files changed, 53 insertions(+), 29 deletions(-) diff --git a/amy/__init__.py b/amy/__init__.py index b8526344..6a3bd4f0 100644 --- a/amy/__init__.py +++ b/amy/__init__.py @@ -5,7 +5,7 @@ import time try: import c_amy as _amy # Import the C module - live = _amy.live + #live = _amy.live except ImportError: # C module is not required, so pass pass diff --git a/src/amy-message.c b/src/amy-message.c index a7ed74f3..47daeb7d 100644 --- a/src/amy-message.c +++ b/src/amy-message.c @@ -48,6 +48,7 @@ int main(int argc, char ** argv) { amy_config.audio = AMY_AUDIO_IS_MINIAUDIO; amy_config.playback_device_id = playback_device_id; amy_config.capture_device_id = capture_device_id; + amy_config.i2s_din = 0; // fake, to indicate has_audio_in. Critical to miniaudio running amy_start(amy_config); //amy_live_start(); @@ -72,7 +73,7 @@ int main(int argc, char ** argv) { } } - amy_live_stop(); + //amy_live_stop(); return 0; } diff --git a/src/amy-piano.c b/src/amy-piano.c index 118299dd..99ef2fe6 100644 --- a/src/amy-piano.c +++ b/src/amy-piano.c @@ -14,10 +14,14 @@ void delay_ms(uint32_t ms) { int main(int argc, char ** argv) { amy_config_t amy_config = amy_default_config(); amy_config.audio = AMY_AUDIO_IS_MINIAUDIO; + //amy_config.playback_device_id = -1; + //amy_config.capture_device_id = -1; + amy_config.i2s_din = 0; // fake, to indicate has_audio_in. Critical to miniaudio running + //amy_config.i2s_dout = 0; // fake, to indicate has_audio_out amy_config.features.default_synths = 0; amy_start(amy_config); - - amy_live_start(); + + //amy_live_start(); amy_add_message("S16384Z"); amy_add_message("t0V5Z"); @@ -183,7 +187,7 @@ int main(int argc, char ** argv) { show_debug(99); - amy_live_stop(); + //amy_live_stop(); return 0; } diff --git a/src/api.c b/src/api.c index 730dfccb..1aa912d5 100644 --- a/src/api.c +++ b/src/api.c @@ -215,12 +215,6 @@ void amy_add_event(amy_event *e) { } - -void amy_stop() { - oscs_deinit(); -} - - #ifdef __EMSCRIPTEN__ void amy_start_web() { // a shim for web AMY, as it's annoying to build structs in js @@ -324,7 +318,8 @@ void amy_bleep_synth(uint32_t start) { amy_add_event(&e); } -extern void *miniaudio_run(void *vargp); +extern void miniaudio_start(); +extern void miniaudio_stop(); void amy_start(amy_config_t c) { global_init(c); @@ -341,13 +336,18 @@ void amy_start(amy_config_t c) { amy_bleep(0); // bleep using raw oscs. } #if !defined(ESP_PLATFORM) && !defined(PICO_ON_DEVICE) && !defined(ARDUINO) - // This is a WASM/libminiaudio setup, we still need live_start. - pthread_t amy_live_thread; - pthread_create(&amy_live_thread, NULL, miniaudio_run, NULL); + miniaudio_start(); +#endif +} + +void amy_stop() { +#if !defined(ESP_PLATFORM) && !defined(PICO_ON_DEVICE) && !defined(ARDUINO) + miniaudio_stop(); #endif - amy_global.running = 1; + oscs_deinit(); } + int16_t *amy_update() { // Single function to update buffers. amy_update_tasks(); @@ -364,3 +364,4 @@ int16_t *amy_update() { } return block; } + diff --git a/src/libminiaudio-audio.c b/src/libminiaudio-audio.c index fabbb788..2f5042b4 100644 --- a/src/libminiaudio-audio.c +++ b/src/libminiaudio-audio.c @@ -230,15 +230,33 @@ amy_err_t miniaudio_init() { return AMY_OK; } +void miniaudio_deinit(void) { + ma_device_uninit(&device); + free(leftover_buf); +} + + void *miniaudio_run(void *vargp) { - miniaudio_init(); - while(amy_global.running) { sleep(1); } return NULL; } +void miniaudio_start(void) { + // This is a WASM/libminiaudio setup, we still need live_start. + miniaudio_init(); + amy_global.running = 1; + pthread_t amy_live_thread; + pthread_create(&amy_live_thread, NULL, miniaudio_run, NULL); +} + +void miniaudio_stop(void) { + amy_global.running = 0; // Causes miniaudio_run thread to exit. + miniaudio_deinit(); +} + + #ifdef __EMSCRIPTEN__ void amy_live_start_web_audioin() { //amy_global.config.features.audio_in = 1; @@ -261,9 +279,9 @@ void amy_live_start_web() { //} -void amy_live_stop() { - amy_global.running = 0; - ma_device_uninit(&device); - free(leftover_buf); -} +//void amy_live_stop() { +// amy_global.running = 0; +// ma_device_uninit(&device); +// free(leftover_buf); +//} #endif diff --git a/src/pyamy.c b/src/pyamy.c index 908b8872..77305a7e 100644 --- a/src/pyamy.c +++ b/src/pyamy.c @@ -32,10 +32,10 @@ static PyObject * live_wrapper(PyObject *self, PyObject *args) { return Py_None; } -static PyObject * pause_wrapper(PyObject *self, PyObject *args) { - amy_live_stop(); - return Py_None; -} +//static PyObject * pause_wrapper(PyObject *self, PyObject *args) { +// amy_live_stop(); +// return Py_None; +//} static PyObject * amystop_wrapper(PyObject *self, PyObject *args) { amy_stop(); @@ -101,8 +101,8 @@ static PyObject * inject_midi_wrapper(PyObject *self, PyObject *args) { static PyMethodDef c_amyMethods[] = { {"render_to_list", render_wrapper, METH_VARARGS, "Render audio"}, {"send_wire", send_wrapper, METH_VARARGS, "Send a message"}, - {"live", live_wrapper, METH_VARARGS, "Live AMY"}, - {"pause", pause_wrapper, METH_VARARGS, "Pause AMY"}, + //{"live", live_wrapper, METH_VARARGS, "Live AMY"}, + //{"pause", pause_wrapper, METH_VARARGS, "Pause AMY"}, {"start_no_default", amystart_no_default_wrapper, METH_VARARGS, "Start AMY"}, {"start", amystart_wrapper, METH_VARARGS, "Start AMY"}, {"stop", amystop_wrapper, METH_VARARGS, "Stop AMY"}, From c10833e3c13795035848690e002a2c6f6a0c5ad8 Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Mon, 8 Dec 2025 20:21:24 -0500 Subject: [PATCH 09/17] Reintroduce _amy.live() to reset devices. --- amy/__init__.py | 2 +- src/amy-example.c | 1 + src/pyamy.c | 13 ++++++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/amy/__init__.py b/amy/__init__.py index 6a3bd4f0..b8526344 100644 --- a/amy/__init__.py +++ b/amy/__init__.py @@ -5,7 +5,7 @@ import time try: import c_amy as _amy # Import the C module - #live = _amy.live + live = _amy.live except ImportError: # C module is not required, so pass pass diff --git a/src/amy-example.c b/src/amy-example.c index 465957d5..b2389515 100644 --- a/src/amy-example.c +++ b/src/amy-example.c @@ -59,6 +59,7 @@ int main(int argc, char ** argv) { amy_config_t amy_config = amy_default_config(); amy_config.audio = AMY_AUDIO_IS_MINIAUDIO; amy_config.playback_device_id = playback_device_id; + fprintf(stderr, "playback_device_id=%d\n", playback_device_id); amy_config.capture_device_id = capture_device_id; amy_config.i2s_din = 0; // fake, to indicate has_audio_in amy_config.i2s_dout = 0; // fake, to indicate has_audio_out diff --git a/src/pyamy.c b/src/pyamy.c index 77305a7e..236b22ea 100644 --- a/src/pyamy.c +++ b/src/pyamy.c @@ -19,16 +19,19 @@ static PyObject * send_wrapper(PyObject *self, PyObject *args) { static PyObject * live_wrapper(PyObject *self, PyObject *args) { + amy_stop(); + amy_config_t amy_config = amy_default_config(); int arg1 = -1; int arg2 = -1; if(PyTuple_Size(args) == 2) { PyArg_ParseTuple(args, "ii", &arg1, &arg2); - amy_global.config.playback_device_id = arg1; - amy_global.config.capture_device_id = arg2; + amy_config.playback_device_id = arg1; + amy_config.capture_device_id = arg2; } else { - amy_global.config.playback_device_id = -1; - amy_global.config.capture_device_id = -1; + amy_config.playback_device_id = -1; + amy_config.capture_device_id = -1; } //amy_live_start(); + amy_start(amy_config); // initializes amy return Py_None; } @@ -101,7 +104,7 @@ static PyObject * inject_midi_wrapper(PyObject *self, PyObject *args) { static PyMethodDef c_amyMethods[] = { {"render_to_list", render_wrapper, METH_VARARGS, "Render audio"}, {"send_wire", send_wrapper, METH_VARARGS, "Send a message"}, - //{"live", live_wrapper, METH_VARARGS, "Live AMY"}, + {"live", live_wrapper, METH_VARARGS, "Live AMY"}, //{"pause", pause_wrapper, METH_VARARGS, "Pause AMY"}, {"start_no_default", amystart_no_default_wrapper, METH_VARARGS, "Start AMY"}, {"start", amystart_wrapper, METH_VARARGS, "Start AMY"}, From bad5c3472bd506c8ab483750c12cff709b12af97 Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Sun, 28 Dec 2025 19:24:04 -0500 Subject: [PATCH 10/17] Merge with main & fix amy-example to work again. --- examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino | 7 ++++--- src/amy-example.c | 9 +++++---- src/amy.h | 13 +++++++------ src/api.c | 4 ++-- src/libminiaudio-audio.c | 17 ++++++++--------- src/libminiaudio-audio.h | 6 +++--- src/sequencer.c | 4 ++++ 7 files changed, 33 insertions(+), 27 deletions(-) diff --git a/examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino b/examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino index 819160be..6024de1c 100644 --- a/examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino +++ b/examples/AMY_MIDI_Synth/AMY_MIDI_Synth.ino @@ -78,11 +78,10 @@ void setup() { // Install the default_synths on synths (MIDI chans) 1, 2, and 10 (this is the default). amy_config.features.default_synths = 1; - // If you want MIDI over UART (5-pin or 3-pin serial MIDI) - amy_config.midi = AMY_MIDI_IS_UART; - // Pins for i2s board // Note: On the Teensy, all these settings are ignored, and blck = 21, lrc = 20, dout = 7. + amy_config.audio = AMY_AUDIO_IS_I2S; + amy_config.features.audio_in = 1; amy_config.i2s_mclk = 7; amy_config.i2s_bclk = 8; // On Pi Pico (RP2040, RP2350), i2s_lrc has to be i2s_bclk + 1, otherwise code will stop on an assert. @@ -90,6 +89,8 @@ void setup() { amy_config.i2s_dout = 10; amy_config.i2s_din = 11; + // If you want MIDI over UART (5-pin or 3-pin serial MIDI) + amy_config.midi = AMY_MIDI_IS_UART; // Pins for UART MIDI // Note: On the Teensy, these are ignored and midi_out = 35, midi_in = 34. amy_config.midi_out = 4; diff --git a/src/amy-example.c b/src/amy-example.c index 091d6f29..42f21b60 100644 --- a/src/amy-example.c +++ b/src/amy-example.c @@ -58,12 +58,13 @@ int main(int argc, char ** argv) { amy_config_t amy_config = amy_default_config(); amy_config.audio = AMY_AUDIO_IS_MINIAUDIO; + amy_config.features.audio_in = 1; // We need audio_in for miniaudio to run?? amy_config.playback_device_id = playback_device_id; fprintf(stderr, "playback_device_id=%d\n", playback_device_id); amy_config.capture_device_id = capture_device_id; - amy_config.i2s_din = 0; // fake, to indicate has_audio_in - amy_config.i2s_dout = 0; // fake, to indicate has_audio_out amy_config.features.default_synths = 0; + + for (int tries = 0; tries < 2; ++tries) { amy_start(amy_config); //example_fm(0); @@ -88,13 +89,13 @@ int main(int argc, char ** argv) { //show_debug(99); - amy_live_stop(); - amy_stop(); // Make sure libminiaudio has time to clean up. sleep(2); + } + return 0; } diff --git a/src/amy.h b/src/amy.h index f1a3dbb9..1c2de5f0 100644 --- a/src/amy.h +++ b/src/amy.h @@ -79,8 +79,8 @@ extern const uint16_t pcm_samples; #define AMY_HAS_STARTUP_BLEEP (amy_global.config.features.startup_bleep) #define AMY_HAS_REVERB (amy_global.config.features.reverb) -#define AMY_HAS_AUDIO_IN (amy_global.config.i2s_din != -1) -#define AMY_HAS_I2S (amy_global.config.i2s_dout != -1) +#define AMY_HAS_AUDIO_IN (amy_global.config.features.audio_in) +#define AMY_HAS_I2S (amy_global.config.audio == AMY_AUDIO_IS_I2S) #define AMY_HAS_DEFAULT_SYNTHS (amy_global.config.features.default_synths) #define AMY_HAS_CHORUS (amy_global.config.features.chorus) #define AMY_HAS_ECHO (amy_global.config.features.echo) @@ -584,6 +584,7 @@ typedef struct { uint8_t chorus : 1; uint8_t reverb : 1; uint8_t echo : 1; + uint8_t audio_in : 1; uint8_t default_synths : 1; uint8_t partials : 1; uint8_t custom : 1; @@ -606,8 +607,8 @@ typedef struct { // pins for MCU platforms int8_t i2s_lrc; - int8_t i2s_dout; // -1 == no I2S - int8_t i2s_din; // -1 == no AUDIO_IN + int8_t i2s_dout; + int8_t i2s_din; int8_t i2s_bclk; int8_t i2s_mclk; uint16_t i2s_mclk_mult; @@ -761,8 +762,8 @@ void amy_add_event(amy_event *e); void amy_parse_message(char * message, int length, amy_event *e); void amy_start(amy_config_t); void amy_stop(); -void amy_live_start(); -void amy_live_stop(); +//void amy_live_start(); +//void amy_live_stop(); int16_t *amy_update(); // in api.c void amy_hardware_init(); // in i2s.c diff --git a/src/api.c b/src/api.c index 1cd7f8b4..7a3563cc 100644 --- a/src/api.c +++ b/src/api.c @@ -74,8 +74,8 @@ amy_config_t amy_default_config() { c.playback_device_id = -1; c.i2s_lrc = -1; - c.i2s_dout = -1; // -1 implies no I2S - c.i2s_din = -1; // -1 implies no AUDIO_IN + c.i2s_dout = -1; + c.i2s_din = -1; c.i2s_bclk = -1; c.i2s_mclk = -1; c.i2s_mclk_mult = 256; // MCLK = mclk_mult * Fs. diff --git a/src/libminiaudio-audio.c b/src/libminiaudio-audio.c index 23fc70ce..8f358755 100644 --- a/src/libminiaudio-audio.c +++ b/src/libminiaudio-audio.c @@ -232,6 +232,7 @@ amy_err_t miniaudio_init() { void miniaudio_deinit(void) { ma_device_uninit(&device); + ma_context_uninit(&context); free(leftover_buf); } @@ -259,14 +260,13 @@ void miniaudio_stop(void) { #ifdef __EMSCRIPTEN__ void amy_live_start_web_audioin() { - //amy_global.config.features.audio_in = 1; - amy_global.config.i2s_din = 0; // Fake, this is how we indicate we have AUDIO_IN + amy_global.config.features.audio_in = 1; emscripten_cancel_main_loop(); miniaudio_init(); emscripten_set_main_loop(main_loop__em, 0, 0); } void amy_live_start_web() { - amy_global.config.i2s_din = -1; // This is how we indicate no AUDIO_IN + amy_global.config.features.audio_in = 0; emscripten_cancel_main_loop(); miniaudio_init(); emscripten_set_main_loop(main_loop__em, 0, 0); @@ -279,10 +279,9 @@ void amy_live_start_web() { //} -void amy_live_stop() { - amy_global.running = 0; - ma_device_uninit(&device); - ma_context_uninit(&context); - free(leftover_buf); -} +//void amy_live_stop() { +// amy_global.running = 0; +// ma_device_uninit(&device); +// ma_context_uninit(&context); +//} #endif diff --git a/src/libminiaudio-audio.h b/src/libminiaudio-audio.h index e2b89bac..863b1405 100644 --- a/src/libminiaudio-audio.h +++ b/src/libminiaudio-audio.h @@ -6,6 +6,6 @@ #include void amy_print_devices(); -void amy_live_start(); -void amy_live_stop(); -#endif \ No newline at end of file +//void amy_live_start(); +//void amy_live_stop(); +#endif diff --git a/src/sequencer.c b/src/sequencer.c index d81a4082..14cee523 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -199,6 +199,10 @@ void _sequencer_start() { add_repeating_timer_us(-500, sequencer_timer_callback, NULL, &pico_sequencer_timer); } +void _sequencer_stop() { + cancel_repeating_timer(&pico_sequencer_timer); +} + #elif defined _POSIX_THREADS #include From f7cb742e40dd55c12c21b08436be326391792ac2 Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Mon, 29 Dec 2025 08:56:00 -0500 Subject: [PATCH 11/17] Rename config.hardware to platform, make it a bitfield struct. --- amy/constants.py | 3 --- src/amy.h | 15 ++++++--------- src/amy_midi.c | 2 +- src/api.c | 9 +++++---- src/i2s.c | 16 ++++++++-------- src/libminiaudio-audio.c | 2 +- src/sequencer.c | 2 +- 7 files changed, 22 insertions(+), 27 deletions(-) diff --git a/amy/constants.py b/amy/constants.py index fb33afd0..06cb0fb4 100644 --- a/amy/constants.py +++ b/amy/constants.py @@ -111,6 +111,3 @@ AMY_MIDI_IS_USB_GADGET=0x02 AMY_MIDI_IS_MACOS=0x04 AMY_MIDI_IS_WEBMIDI=0x08 -AMY_HARDWARE_NONE=0x00 -AMY_HARDWARE_MULTICORE=0x01 -AMY_HARDWARE_MULTITHREAD=0x02 diff --git a/src/amy.h b/src/amy.h index 1c2de5f0..506ad954 100644 --- a/src/amy.h +++ b/src/amy.h @@ -571,13 +571,6 @@ typedef struct delay_line { #define AMY_MIDI_IS_MACOS 0x04 #define AMY_MIDI_IS_WEBMIDI 0x08 -// Values for config.hardware -#define AMY_HARDWARE_NONE 0x00 -// Use secondary cores if available. -#define AMY_HARDWARE_MULTICORE 0x01 -// Use multitasking (e.g. RTOS threads) if available. -#define AMY_HARDWARE_MULTITHREAD 0x02 - typedef struct { struct { @@ -590,9 +583,13 @@ typedef struct { uint8_t custom : 1; uint8_t startup_bleep : 1; } features; + struct { + uint8_t multicore : 1; // Use secondary cores if available. + uint8_t multithread : 1; // Use multitasking (e.g. RTOS threads) if available. + } platform; + // midi and audio are potentially bitmasks indicating data routing. uint8_t midi; uint8_t audio; - uint32_t hardware; // variables uint16_t max_oscs; @@ -766,7 +763,7 @@ void amy_stop(); //void amy_live_stop(); int16_t *amy_update(); // in api.c -void amy_hardware_init(); // in i2s.c +void amy_platform_init(); // in i2s.c void amy_update_tasks(); // in i2s.c int16_t *amy_render_audio(); // in i2s.c size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes); // in i2s.c diff --git a/src/amy_midi.c b/src/amy_midi.c index 1d40b933..8bb184a4 100644 --- a/src/amy_midi.c +++ b/src/amy_midi.c @@ -391,7 +391,7 @@ void run_midi() { sysex_buffer = malloc_caps(MAX_SYSEX_BYTES, amy_global.config.ram_caps_sysex); if(amy_global.config.midi & AMY_MIDI_IS_UART) { esp_init_midi(); - if (amy_global.config.hardware & AMY_HARDWARE_MULTITHREAD) { + if (amy_global.config.platform.multithread) { xTaskCreatePinnedToCore(run_midi_task, MIDI_TASK_NAME, (MIDI_TASK_STACK_SIZE) / sizeof(StackType_t), NULL, MIDI_TASK_PRIORITY, &midi_handle, MIDI_TASK_COREID); } // otherwise esp_poll_midi is called from amy_update_tasks() } diff --git a/src/api.c b/src/api.c index 7a3563cc..b9ebacb9 100644 --- a/src/api.c +++ b/src/api.c @@ -32,8 +32,9 @@ amy_config_t amy_default_config() { c.features.default_synths = 1; c.features.startup_bleep = 0; - // Use all hardware features by default. - c.hardware = AMY_HARDWARE_MULTICORE | AMY_HARDWARE_MULTITHREAD; + // Use all platform features by default. + c.platform.multicore = 1; + c.platform.multithread = 1; c.write_samples_fn = NULL; @@ -325,8 +326,8 @@ void amy_start(amy_config_t c) { global_init(c); amy_profiles_init(); oscs_init(); - amy_hardware_init(); - run_midi(); // Must be after hardware_init in case F_CPU is modified on RP2040 Arduino. + amy_platform_init(); + run_midi(); // Must be after platform_init in case F_CPU is modified on RP2040 Arduino. if(AMY_HAS_DEFAULT_SYNTHS) amy_default_synths(); if(AMY_HAS_STARTUP_BLEEP) { if(AMY_HAS_DEFAULT_SYNTHS) diff --git a/src/i2s.c b/src/i2s.c index d50bb74a..73e3548d 100644 --- a/src/i2s.c +++ b/src/i2s.c @@ -197,12 +197,12 @@ void esp_fill_audio_buffer_task() { } // init AMY from the esp. wraps some amy funcs in a task to do multicore rendering on the ESP32 -void amy_hardware_init() { +void amy_platform_init() { // Start i2s if (AMY_HAS_I2S) { esp32_setup_i2s(); } - if (amy_global.config.hardware & AMY_HARDWARE_MULTITHREAD) { + if (amy_global.config.platform.multithread) { // Create the second core rendering task xTaskCreatePinnedToCore(&esp_render_task, AMY_RENDER_TASK_NAME, AMY_RENDER_TASK_STACK_SIZE, NULL, AMY_RENDER_TASK_PRIORITY, &amy_render_handle, AMY_RENDER_TASK_COREID); // And the fill audio buffer thread, combines, does volume & filters @@ -213,7 +213,7 @@ void amy_hardware_init() { extern void esp_poll_midi(void); void amy_update_tasks() { - if ((amy_global.config.hardware & AMY_HARDWARE_MULTITHREAD) == 0) { + if (!amy_global.config.platform.multithread) { amy_execute_deltas(); esp_poll_midi(); } else{ @@ -223,7 +223,7 @@ void amy_update_tasks() { int16_t *amy_render_audio() { int16_t *buf = NULL; - if ((amy_global.config.hardware & AMY_HARDWARE_MULTITHREAD) == 0) { + if (!amy_global.config.platform.multithread) { amy_render(0, AMY_OSCS, 0); buf = amy_fill_buffer(); } else { @@ -305,7 +305,7 @@ int32_t render_other_core(int32_t data) { int16_t *amy_render_audio() { #ifdef USE_SECOND_CORE - if (amy_global.config.hardware & AMY_HARDWARE_MULTICORE) { + if (amy_global.config.platform.multicore) { int32_t res; queue_entry_t entry = {render_other_core, AMY_OK}; queue_add_blocking(&call_queue, &entry); @@ -370,12 +370,12 @@ void core1_main() { } } -void amy_hardware_init() { +void amy_platform_init() { if (AMY_HAS_I2S) { pico_setup_i2s(&amy_global.config); } #ifdef USE_SECOND_CORE - if (amy_global.config.hardware & AMY_HARDWARE_MULTICORE) { + if (amy_global.config.platform.multicore) { queue_init(&call_queue, sizeof(queue_entry_t), 2); queue_init(&results_queue, sizeof(int32_t), 2); uint32_t * core1_separate_stack_address = (uint32_t*)malloc(0x2000); @@ -394,7 +394,7 @@ extern size_t teensy_i2s_write(const uint8_t *buffer, size_t nbytes); extern int16_t teensy_get_serial_byte(); -void amy_hardware_init() { +void amy_platform_init() { if (AMY_HAS_I2S) { teensy_setup_i2s(); } diff --git a/src/libminiaudio-audio.c b/src/libminiaudio-audio.c index 8f358755..6d6a3568 100644 --- a/src/libminiaudio-audio.c +++ b/src/libminiaudio-audio.c @@ -56,7 +56,7 @@ int16_t *volatile last_audio_buffer = NULL; void amy_update_tasks() { } -void amy_hardware_init() { +void amy_platform_init() { } size_t amy_i2s_write(const uint8_t *buffer, size_t nbytes) { diff --git a/src/sequencer.c b/src/sequencer.c index 14cee523..8c9390b8 100644 --- a/src/sequencer.c +++ b/src/sequencer.c @@ -178,7 +178,7 @@ void _sequencer_start() { void _sequencer_stop() { if (periodic_timer) { - ESP_ERRIR_CHECK(esp_timer_stop(periodic_timer)); + ESP_ERROR_CHECK(esp_timer_stop(periodic_timer)); ESP_ERROR_CHECK(esp_timer_delete(periodic_timer)); periodic_timer = NULL; } From ba8743ba27adecbd42e1a41af8d88975a6bf634b Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Tue, 30 Dec 2025 16:01:25 -0500 Subject: [PATCH 12/17] Fixing AMY_Arduino to work on ESP32. --- src/amy.c | 1 + src/amy.h | 1 + src/api.c | 2 +- src/i2s.c | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/amy.c b/src/amy.c index 3489ec86..b560b54f 100644 --- a/src/amy.c +++ b/src/amy.c @@ -361,6 +361,7 @@ int peek_stack(char *tag) { return 0; } int8_t global_init(amy_config_t c) { peek_stack("init"); amy_global.config = c; + amy_global.i2s_is_in_background = 0; amy_global.delta_queue = NULL; amy_global.delta_qsize = 0; amy_global.volume = 1.0f; diff --git a/src/amy.h b/src/amy.h index 506ad954..242275c7 100644 --- a/src/amy.h +++ b/src/amy.h @@ -655,6 +655,7 @@ typedef struct echo_config { struct state { amy_config_t config; uint8_t running; + uint8_t i2s_is_in_background; // Flag not to handle I2S in amy_update. float volume; float pitch_bend; // State of fixed dc-blocking HPF diff --git a/src/api.c b/src/api.c index b9ebacb9..1813e251 100644 --- a/src/api.c +++ b/src/api.c @@ -352,7 +352,7 @@ int16_t *amy_update() { // Single function to update buffers. amy_update_tasks(); int16_t *block = amy_render_audio(); - if (AMY_HAS_I2S) { + if (AMY_HAS_I2S && !amy_global.i2s_is_in_background) { amy_i2s_write( (uint8_t *)block, AMY_BLOCK_SIZE * AMY_NCHANS * sizeof(int16_t) ); diff --git a/src/i2s.c b/src/i2s.c index 73e3548d..5ec9786c 100644 --- a/src/i2s.c +++ b/src/i2s.c @@ -207,6 +207,8 @@ void amy_platform_init() { xTaskCreatePinnedToCore(&esp_render_task, AMY_RENDER_TASK_NAME, AMY_RENDER_TASK_STACK_SIZE, NULL, AMY_RENDER_TASK_PRIORITY, &amy_render_handle, AMY_RENDER_TASK_COREID); // And the fill audio buffer thread, combines, does volume & filters xTaskCreatePinnedToCore(&esp_fill_audio_buffer_task, AMY_FILL_BUFFER_TASK_NAME, AMY_FILL_BUFFER_TASK_STACK_SIZE, NULL, AMY_FILL_BUFFER_TASK_PRIORITY, &amy_fill_buffer_handle, AMY_FILL_BUFFER_TASK_COREID); + // Let amy_update know we have I2S covered. + amy_global.i2s_is_in_background = 1; } } From 45ba17cc703251299f22b4aedb41fc35c0980137 Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Thu, 1 Jan 2026 11:06:28 -0500 Subject: [PATCH 13/17] i2s.c: trying to make esp32 work without background i2s. --- src/i2s.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i2s.c b/src/i2s.c index 5ec9786c..66bc14fb 100644 --- a/src/i2s.c +++ b/src/i2s.c @@ -208,7 +208,9 @@ void amy_platform_init() { // And the fill audio buffer thread, combines, does volume & filters xTaskCreatePinnedToCore(&esp_fill_audio_buffer_task, AMY_FILL_BUFFER_TASK_NAME, AMY_FILL_BUFFER_TASK_STACK_SIZE, NULL, AMY_FILL_BUFFER_TASK_PRIORITY, &amy_fill_buffer_handle, AMY_FILL_BUFFER_TASK_COREID); // Let amy_update know we have I2S covered. - amy_global.i2s_is_in_background = 1; + if (AMY_HAS_I2S) { + amy_global.i2s_is_in_background = 1; + } } } From 42908a6e86553e740a8876e9ec673057ad8d1611 Mon Sep 17 00:00:00 2001 From: Brian Whitman Date: Thu, 1 Jan 2026 10:15:42 -0800 Subject: [PATCH 14/17] fixing example back --- src/amy-example.c | 47 +++++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/src/amy-example.c b/src/amy-example.c index 1942f9f8..86bc9e44 100644 --- a/src/amy-example.c +++ b/src/amy-example.c @@ -58,51 +58,35 @@ int main(int argc, char ** argv) { amy_config_t amy_config = amy_default_config(); amy_config.audio = AMY_AUDIO_IS_MINIAUDIO; + amy_config.features.audio_in = 1; // We need audio_in for miniaudio to run?? amy_config.playback_device_id = playback_device_id; + fprintf(stderr, "playback_device_id=%d\n", playback_device_id); amy_config.capture_device_id = capture_device_id; amy_config.features.default_synths = 0; - amy_start(amy_config); - - amy_live_start(); - amy_add_message("zF1024,sounds/sleepwalk.wav,60"); - amy_add_message("zF1025,sounds/sleepwalk.wav,60"); - - - amy_event e = amy_default_event(); - e.wave = PCM_LEFT; - e.preset = 1024; - e.velocity=1; - e.pan_coefs[0] = 0; - e.midi_note = 60; - e.osc = 14; - amy_add_event(&e); - - e.wave = PCM_RIGHT; - e.preset = 1025; - e.velocity=1; - e.pan_coefs[0] = 1; - e.midi_note = 60; - e.osc = 15; - amy_add_event(&e); + for (int tries = 0; tries < 2; ++tries) { + amy_start(amy_config); - //example_fm(0); //example_voice_chord(0,0); - //example_synth_chord(0, /* patch */ 0); + example_synth_chord(0, /* patch */ 0); //example_sustain_pedal(0, /* patch */ 256); //example_sequencer_drums(0); //example_patch_from_events(); + // Check that trying to program a non-user patch doesn't crash + amy_event e = amy_default_event(); + e.patch_number = 25; + e.osc = 0; + e.wave = SINE; + amy_add_event(&e); - // Now just spin for 15s + // Now just spin for a whilw uint32_t start = amy_sysclock(); - while(amy_sysclock() - start < 30000) { + while(amy_sysclock() - start < 5000) { usleep(THREAD_USLEEP); } - //show_debug(99); - //show_debug(99); amy_stop(); @@ -110,10 +94,9 @@ int main(int argc, char ** argv) { // Make sure libminiaudio has time to clean up. sleep(2); - // Make sure libminiaudio has time to clean up. - sleep(2); + } return 0; } -#endif +#endif \ No newline at end of file From 54d53575814a78809d73914ad489bb89cbe9a2a4 Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Thu, 1 Jan 2026 18:48:11 -0500 Subject: [PATCH 15/17] fix typo --- src/amy-example.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/amy-example.c b/src/amy-example.c index 86bc9e44..fe92f5bf 100644 --- a/src/amy-example.c +++ b/src/amy-example.c @@ -81,7 +81,7 @@ int main(int argc, char ** argv) { e.wave = SINE; amy_add_event(&e); - // Now just spin for a whilw + // Now just spin for a while uint32_t start = amy_sysclock(); while(amy_sysclock() - start < 5000) { usleep(THREAD_USLEEP); @@ -99,4 +99,4 @@ int main(int argc, char ** argv) { return 0; } -#endif \ No newline at end of file +#endif From 23e235a58df302d3d7fc588f734d01a3dfd31c8d Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Thu, 1 Jan 2026 19:22:43 -0500 Subject: [PATCH 16/17] Cleanup cruft; make sure cPython amy.live() works. --- src/amy-example.c | 2 +- src/amy-message.c | 3 ++- src/amy-piano.c | 5 +---- src/amy.h | 2 -- src/api.c | 1 + src/libminiaudio-audio.c | 20 ++++++-------------- src/libminiaudio-audio.h | 2 -- src/pyamy.c | 10 ++-------- 8 files changed, 13 insertions(+), 32 deletions(-) diff --git a/src/amy-example.c b/src/amy-example.c index fe92f5bf..af713fe6 100644 --- a/src/amy-example.c +++ b/src/amy-example.c @@ -62,7 +62,7 @@ int main(int argc, char ** argv) { amy_config.playback_device_id = playback_device_id; fprintf(stderr, "playback_device_id=%d\n", playback_device_id); amy_config.capture_device_id = capture_device_id; - amy_config.features.default_synths = 0; + amy_config.features.default_synths = 1; for (int tries = 0; tries < 2; ++tries) { amy_start(amy_config); diff --git a/src/amy-message.c b/src/amy-message.c index 47daeb7d..2fe35d10 100644 --- a/src/amy-message.c +++ b/src/amy-message.c @@ -46,9 +46,10 @@ int main(int argc, char ** argv) { amy_config_t amy_config = amy_default_config(); amy_config.audio = AMY_AUDIO_IS_MINIAUDIO; + amy_config.features.audio_in = 1; // We need audio_in for miniaudio to run?? + amy_config.features.default_synths = 0; amy_config.playback_device_id = playback_device_id; amy_config.capture_device_id = capture_device_id; - amy_config.i2s_din = 0; // fake, to indicate has_audio_in. Critical to miniaudio running amy_start(amy_config); //amy_live_start(); diff --git a/src/amy-piano.c b/src/amy-piano.c index 99ef2fe6..fe9955a7 100644 --- a/src/amy-piano.c +++ b/src/amy-piano.c @@ -16,13 +16,10 @@ int main(int argc, char ** argv) { amy_config.audio = AMY_AUDIO_IS_MINIAUDIO; //amy_config.playback_device_id = -1; //amy_config.capture_device_id = -1; - amy_config.i2s_din = 0; // fake, to indicate has_audio_in. Critical to miniaudio running - //amy_config.i2s_dout = 0; // fake, to indicate has_audio_out + amy_config.features.audio_in = 1; // Needed to make libminiaudio work? amy_config.features.default_synths = 0; amy_start(amy_config); - //amy_live_start(); - amy_add_message("S16384Z"); amy_add_message("t0V5Z"); amy_add_message("K1024uv0w10Zv21w9ZZ"); diff --git a/src/amy.h b/src/amy.h index 8621ab2f..beef61b7 100644 --- a/src/amy.h +++ b/src/amy.h @@ -788,8 +788,6 @@ void amy_add_event(amy_event *e); void amy_parse_message(char * message, int length, amy_event *e); void amy_start(amy_config_t); void amy_stop(); -//void amy_live_start(); -//void amy_live_stop(); int16_t *amy_update(); // in api.c void amy_platform_init(); // in i2s.c diff --git a/src/api.c b/src/api.c index 1b93c380..3c35a00c 100644 --- a/src/api.c +++ b/src/api.c @@ -40,6 +40,7 @@ amy_config_t amy_default_config() { c.features.partials = 1; c.features.custom = 1; c.features.default_synths = 1; + c.features.audio_in = 0; c.features.startup_bleep = 0; // Use all platform features by default. diff --git a/src/libminiaudio-audio.c b/src/libminiaudio-audio.c index 6d6a3568..4bbcef9b 100644 --- a/src/libminiaudio-audio.c +++ b/src/libminiaudio-audio.c @@ -163,6 +163,9 @@ ma_uint32 captureCount; amy_err_t miniaudio_init() { leftover_buf = malloc_caps(sizeof(int16_t)*AMY_BLOCK_SIZE*AMY_NCHANS, amy_global.config.ram_caps_fbl); + fprintf(stderr, "miniaudio_init: has_audio_in %d playback_id %d capture_id %d\n", + AMY_HAS_AUDIO_IN, amy_global.config.playback_device_id, amy_global.config.capture_device_id); + if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { printf("Failed to setup context for device list.\n"); exit(1); @@ -271,17 +274,6 @@ void amy_live_start_web() { miniaudio_init(); emscripten_set_main_loop(main_loop__em, 0, 0); } -#endif -//void amy_live_start() { -// // kick off a thread running miniaudio_run -// amy_global.running = 1; -// pthread_create(&amy_live_thread, NULL, miniaudio_run, NULL); -//} - - -//void amy_live_stop() { -// amy_global.running = 0; -// ma_device_uninit(&device); -// ma_context_uninit(&context); -//} -#endif +#endif // __EMSCRIPTEN__ + +#endif // Not ESP_PLATFORM or PICO_ON_DEVICE or ARDUINO diff --git a/src/libminiaudio-audio.h b/src/libminiaudio-audio.h index 863b1405..5928d02a 100644 --- a/src/libminiaudio-audio.h +++ b/src/libminiaudio-audio.h @@ -6,6 +6,4 @@ #include void amy_print_devices(); -//void amy_live_start(); -//void amy_live_stop(); #endif diff --git a/src/pyamy.c b/src/pyamy.c index 236b22ea..f36568e3 100644 --- a/src/pyamy.c +++ b/src/pyamy.c @@ -30,16 +30,12 @@ static PyObject * live_wrapper(PyObject *self, PyObject *args) { amy_config.playback_device_id = -1; amy_config.capture_device_id = -1; } - //amy_live_start(); + amy_config.audio = AMY_AUDIO_IS_MINIAUDIO; + amy_config.features.audio_in = 1; // Needed to make miniaudio run amy_start(amy_config); // initializes amy return Py_None; } -//static PyObject * pause_wrapper(PyObject *self, PyObject *args) { -// amy_live_stop(); -// return Py_None; -//} - static PyObject * amystop_wrapper(PyObject *self, PyObject *args) { amy_stop(); return Py_None; @@ -70,7 +66,6 @@ static PyObject * config_wrapper(PyObject *self, PyObject *args) { static PyObject * render_wrapper(PyObject *self, PyObject *args) { int16_t * result = amy_simple_fill_buffer(); - //int16_t * result = amy_update(); // Create a python list of ints (they are signed shorts that come back) uint16_t bs = AMY_BLOCK_SIZE; if(AMY_NCHANS == 2) { @@ -105,7 +100,6 @@ static PyMethodDef c_amyMethods[] = { {"render_to_list", render_wrapper, METH_VARARGS, "Render audio"}, {"send_wire", send_wrapper, METH_VARARGS, "Send a message"}, {"live", live_wrapper, METH_VARARGS, "Live AMY"}, - //{"pause", pause_wrapper, METH_VARARGS, "Pause AMY"}, {"start_no_default", amystart_no_default_wrapper, METH_VARARGS, "Start AMY"}, {"start", amystart_wrapper, METH_VARARGS, "Start AMY"}, {"stop", amystop_wrapper, METH_VARARGS, "Stop AMY"}, From b789c5cb64f8093919c34f6d07bb117f7e7dec76 Mon Sep 17 00:00:00 2001 From: Dan Ellis Date: Thu, 1 Jan 2026 20:05:30 -0500 Subject: [PATCH 17/17] More cleanup; updated api.md and arduino.md for remove of 'live' etc. --- amy/__init__.py | 4 ++-- amy/test.py | 21 +++++++++------------ docs/api.md | 21 ++++++++++----------- docs/arduino.md | 5 ++--- src/amy-message.c | 3 --- src/amy-piano.c | 2 -- src/api.c | 2 +- src/libminiaudio-audio.c | 4 ++-- src/pyamy.c | 15 ++++++--------- 9 files changed, 32 insertions(+), 45 deletions(-) diff --git a/amy/__init__.py b/amy/__init__.py index afafa88c..2f8e1ab3 100644 --- a/amy/__init__.py +++ b/amy/__init__.py @@ -292,9 +292,9 @@ def render(seconds): frames.append( np.array(_amy.render_to_list())/32768.0 ) return np.hstack(frames).reshape((-1, AMY_NCHANS)) -def restart(): +def restart(default_synths=0): _amy.stop() - _amy.start() + _amy.start(default_synths) def inject_midi(a, b, c, d=None): if d is None: diff --git a/amy/test.py b/amy/test.py index 9d1d57c8..aeb24ea6 100644 --- a/amy/test.py +++ b/amy/test.py @@ -37,15 +37,12 @@ class AmyTest: test_dir = './tests/tst' def __init__(self): - self.config_default = False + self.default_synths = False def test(self): name = self.__class__.__name__ _amy.stop() - if self.config_default: - _amy.start() - else: - _amy.start_no_default() + _amy.start(1 if self.default_synths else 0) self.run() samples = amy.render(1.0) @@ -640,7 +637,7 @@ class TestVoiceStealing(AmyTest): def __init__(self): super().__init__() - self.config_default = True + self.default_synths = True def run(self): # Default juno synth. @@ -688,7 +685,7 @@ class TestMidiDrums(AmyTest): def __init__(self): super().__init__() - self.config_default = True + self.default_synths = True def run(self): # inject_midi args are (time, midi_event_chan, midi_note, midi_vel) @@ -704,7 +701,7 @@ class TestDefaultChan1Synth(AmyTest): def __init__(self): super().__init__() - self.config_default = True + self.default_synths = True def run(self): amy.send(time=100, synth=1, note=60, vel=1) @@ -722,7 +719,7 @@ class TestSynthProgChange(AmyTest): def __init__(self): super().__init__() - self.config_default = True + self.default_synths = True def run(self): # DX7 first patch, uses 9 oscs/voice, num_voices is inherited from previous init. @@ -742,7 +739,7 @@ class TestSynthDrums(AmyTest): def __init__(self): super().__init__() - self.config_default = True + self.default_synths = True def run(self): amy.send(time=100, synth=10, note=35, vel=100/127) # bass @@ -799,7 +796,7 @@ class TestPatchFromEvents(AmyTest): """Test defining a patch from events with patch_number.""" def __init__(self): super().__init__() - self.config_default = True # So that the patch space is already partly populated. + self.default_synths = True # So that the patch space is already partly populated. def run(self): amy.send(time=0, patch=1039, reset=amy.RESET_PATCH) @@ -838,7 +835,7 @@ class TestFileTransfer(AmyTest): def test(self): _amy.stop() - _amy.start_no_default() + _amy.start(0) payload = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(2048)) with tempfile.NamedTemporaryFile(mode='w', delete=True) as f: with tempfile.NamedTemporaryFile(mode='w+', delete=True) as g: diff --git a/docs/api.md b/docs/api.md index 2351b0c6..85b7c00f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -47,20 +47,16 @@ uint32_t amy_sysclock(); Start and stop AMY: ```c -amy_stop(); - // Emscripten web start amy_start_web(); amy_start_web_no_synths(); -// Start AMY with a config +// Start AMY with a config. If c.audio is set, will attempt to start live audio amy_start(amy_config_t c); -// Start playing audio in real time -amy_live_start(); +// Stop AMY including any live audio output +amy_stop(); -// Stop playing audio -amy_live_stop(); ``` Default MIDI handlers: @@ -82,14 +78,17 @@ amy_start(amy_config); | ------ | ------ | -------- | -------- | | `features.chorus` | `0=off, 1=on` | On | If chorus is enabled (uses RAM) | | `features.reverb` | `0=off, 1=on` | On | If reverb is enabled (uses RAM) | -| `features.echo` | `0=off, 1=on` |On | If echo is enabled (uses RAM) | -| `features.audio_in` | `0=off, 1=on` | On | If audio_in gets processed via the audio interface | -| `features.default_synths` | `0=off, 1=on` | On| If AMY boots with Juno-6 on `synth` 1 and GM drums on `synth` 10 | +| `features.echo` | `0=off, 1=on` | On | If echo is enabled (uses RAM) | | `features.partials` | `0=off, 1=on` | On | If partials are enabled | | `features.custom` | `0=off, 1=on` | On | If custom C oscillators are enabled | -| `features.startup_bleep` | `0=off, 1=on` | On | If AMY plays a startup sound on boot | +| `features.audio_in` | `0=off, 1=on` | Off | If audio_in gets processed via the audio interface. Must be 1 for AUDIO_IS_MINIAUDIO | +| `features.default_synths` | `0=off, 1=on` | Off| If AMY boots with Juno-6 on `synth` 1 and GM drums on `synth` 10 | +| `features.startup_bleep` | `0=off, 1=on` | Off | If AMY plays a startup sound on boot | +| `platform.multicore | `0=off, 1=on` | On | Attempts to use 2nd core if available | +| `platform.multithread | `0=off, 1=on` | On | Attempts to multithreading if available (ESP/RTOS) | | `midi` | `AMY_MIDI_IS_NONE`, `AMY_MIDI_IS_UART`, `AMY_MIDI_IS_USB_GADGET`, `AMY_MIDI_IS_WEBMIDI` | `AMY_MIDI_IS_NONE` | Which MIDI interface(s) are active | | `audio` | `AMY_AUDIO_IS_NONE`, `AMY_AUDIO_IS_I2S`, `AMY_AUDIO_IS_USB_GADGET`, `AMY_AUDIO_IS_MINIAUDIO`| I2S or miniaudio | Which audio interface(s) are active | +| `write_samples_fn` | fn ptr | `NULL` | If provided, `amy_update` will call this with each new block of samples | | `max_oscs` | Int | 180 | How many oscillators to support | | `max_sequencer_tags` | Int | 256 | How many sequencer items to handle | | `max_voices` | Int | 64 | How many voices | diff --git a/docs/arduino.md b/docs/arduino.md index b2a30ef7..d441f54f 100644 --- a/docs/arduino.md +++ b/docs/arduino.md @@ -4,7 +4,7 @@ AMY is on the Arduino Libraries repository. Simply search for "AMY" in the Libra -We recommend always using the latest released version (now `1.1.0`) in the Arduino Library Manager. +We recommend always using the latest released version (now `1.1.4`) in the Arduino Library Manager. However, if you are directed to a bleeding edge release of AMY, you can simply copy this repository to your `Arduino/libraries` folder as `Arduino/libraries/amy`. (Make sure you delete whatever you already had in `libraries/amy`.) @@ -18,7 +18,6 @@ void setup() { amy_config_t amy_config = amy_default_config(); // set your pins, etc -- see the AMY_MIDI_Synth example amy_start(amy_config); - amy_live_start(); // if you want us to handle sending audio } void loop() { @@ -29,7 +28,7 @@ void loop() { AMY in Arduino handles MIDI input and output as well as I2S and USB (where supported) audio. You do not need to set up an I2S interface, just the pins in `amy_config`. -(For experts: if you're rendering AMY audio to your own audio hardware, you can omit the call to `amy_live_start()` and call our [`amy_simple_fill_buffer()` API](api.md) in `loop()` instead. This gives you a buffer of stereo 16-bit short integers to send out to whatever interface you set up.) +(For experts: if you're rendering AMY audio to your own audio hardware, you can set `amy_config.audio = AMY_AUDIO_IS_NONE` before calling `amy_start()`, then use the return value from `amy_update()` as a pointer to the next block of `AMY_BLOCK_SIZE` 16 bit stereo sample frames, to send out to whatever interface you set up.) [Please see our latest matrix of supported features per chip/board.](https://github.com/shorepine/amy/issues/354) diff --git a/src/amy-message.c b/src/amy-message.c index 2fe35d10..8151d518 100644 --- a/src/amy-message.c +++ b/src/amy-message.c @@ -51,7 +51,6 @@ int main(int argc, char ** argv) { amy_config.playback_device_id = playback_device_id; amy_config.capture_device_id = capture_device_id; amy_start(amy_config); - //amy_live_start(); while (1) { char input[1024]; @@ -74,8 +73,6 @@ int main(int argc, char ** argv) { } } - //amy_live_stop(); - return 0; } #endif diff --git a/src/amy-piano.c b/src/amy-piano.c index fe9955a7..fcc6056b 100644 --- a/src/amy-piano.c +++ b/src/amy-piano.c @@ -184,8 +184,6 @@ int main(int argc, char ** argv) { show_debug(99); - //amy_live_stop(); - return 0; } diff --git a/src/api.c b/src/api.c index 3c35a00c..9b496631 100644 --- a/src/api.c +++ b/src/api.c @@ -39,7 +39,7 @@ amy_config_t amy_default_config() { c.features.chorus = 1; c.features.partials = 1; c.features.custom = 1; - c.features.default_synths = 1; + c.features.default_synths = 0; c.features.audio_in = 0; c.features.startup_bleep = 0; diff --git a/src/libminiaudio-audio.c b/src/libminiaudio-audio.c index 4bbcef9b..1668d899 100644 --- a/src/libminiaudio-audio.c +++ b/src/libminiaudio-audio.c @@ -163,8 +163,8 @@ ma_uint32 captureCount; amy_err_t miniaudio_init() { leftover_buf = malloc_caps(sizeof(int16_t)*AMY_BLOCK_SIZE*AMY_NCHANS, amy_global.config.ram_caps_fbl); - fprintf(stderr, "miniaudio_init: has_audio_in %d playback_id %d capture_id %d\n", - AMY_HAS_AUDIO_IN, amy_global.config.playback_device_id, amy_global.config.capture_device_id); + //fprintf(stderr, "miniaudio_init: has_audio_in %d playback_id %d capture_id %d\n", + // AMY_HAS_AUDIO_IN, amy_global.config.playback_device_id, amy_global.config.capture_device_id); if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { printf("Failed to setup context for device list.\n"); diff --git a/src/pyamy.c b/src/pyamy.c index f36568e3..56553261 100644 --- a/src/pyamy.c +++ b/src/pyamy.c @@ -20,7 +20,7 @@ static PyObject * send_wrapper(PyObject *self, PyObject *args) { static PyObject * live_wrapper(PyObject *self, PyObject *args) { amy_stop(); - amy_config_t amy_config = amy_default_config(); + amy_config_t amy_config = amy_global.config; int arg1 = -1; int arg2 = -1; if(PyTuple_Size(args) == 2) { PyArg_ParseTuple(args, "ii", &arg1, &arg2); @@ -42,14 +42,12 @@ static PyObject * amystop_wrapper(PyObject *self, PyObject *args) { } static PyObject * amystart_wrapper(PyObject *self, PyObject *args) { + int default_synths = 0; + if(PyTuple_Size(args) == 1) { + PyArg_ParseTuple(args, "i", &default_synths); + } amy_config_t amy_config = amy_default_config(); - amy_start(amy_config); // initializes amy - return Py_None; -} - -static PyObject * amystart_no_default_wrapper(PyObject *self, PyObject *args) { - amy_config_t amy_config = amy_default_config(); - amy_config.features.default_synths = 0; + amy_config.features.default_synths = default_synths; amy_start(amy_config); // initializes amy return Py_None; } @@ -100,7 +98,6 @@ static PyMethodDef c_amyMethods[] = { {"render_to_list", render_wrapper, METH_VARARGS, "Render audio"}, {"send_wire", send_wrapper, METH_VARARGS, "Send a message"}, {"live", live_wrapper, METH_VARARGS, "Live AMY"}, - {"start_no_default", amystart_no_default_wrapper, METH_VARARGS, "Start AMY"}, {"start", amystart_wrapper, METH_VARARGS, "Start AMY"}, {"stop", amystop_wrapper, METH_VARARGS, "Stop AMY"}, {"config", config_wrapper, METH_VARARGS, "Return config"},