diff --git a/src/amy.c b/src/amy.c index e3897ba8..6d93239b 100644 --- a/src/amy.c +++ b/src/amy.c @@ -890,9 +890,7 @@ void amy_event_to_deltas_queue(amy_event *e, uint16_t base_osc, uint16_t oscs_pe // The load itself does not: it takes tens of ms on an ESP32-S3 // (a 6-voice DX7 load ~50-70 ms), and a render held off that long // runs the DMA ring dry. The flush is µs. - amy_grab_render_lock(); - flush_due_deltas(); - amy_release_render_lock(); + amy_settle_deltas(); patches_load_patch(e); } // Execute any other commands in this event. @@ -2445,6 +2443,17 @@ static void flush_due_deltas() { amy_release_lock(); } +// Play the deltas that are due, from any thread, without advancing the +// sequencer: what ingest calls before an operation that a queued reset must +// not land after (a patch load rebuilding the synth tables, a sample load +// that amy_reset_oscs() would unload). Under the render lock, because the +// flush can free oscs a render in progress is reading. +void amy_settle_deltas() { + amy_grab_render_lock(); + flush_due_deltas(); + amy_release_render_lock(); +} + // this takes scheduled deltas and plays them at the right time void amy_execute_deltas() { AMY_PROFILE_START(AMY_EXECUTE_DELTAS) @@ -2453,13 +2462,10 @@ void amy_execute_deltas() { sequencer_check_and_fill(); // Make sure any CV-triggered events are added to delta queue update_external_cv_in(); - // The flush can free oscs, so it runs under the render lock. Render loops - // already hold it across the whole block (it's recursive for them); this - // covers callers that execute deltas off the render thread (parse.c's - // sample-transfer start). - amy_grab_render_lock(); - flush_due_deltas(); - amy_release_render_lock(); + // Render loops already hold the render lock across the whole block (it's + // recursive for them); amy_settle_deltas() takes it anyway, for render + // loops that call this before taking it for the render itself. + amy_settle_deltas(); AMY_PROFILE_STOP(AMY_EXECUTE_DELTAS) } diff --git a/src/amy.h b/src/amy.h index 2f65e7fe..77277fff 100644 --- a/src/amy.h +++ b/src/amy.h @@ -1206,6 +1206,7 @@ void patches_deinit(); void parse_algo_source(char* message, int16_t *vals); void hold_and_modify(uint16_t osc) ; void amy_execute_deltas(); +void amy_settle_deltas(); // due deltas only, any thread: no sequencer tick 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/parse.c b/src/parse.c index f29926c6..0eb87230 100644 --- a/src/parse.c +++ b/src/parse.c @@ -588,7 +588,11 @@ uint16_t amy_parse_transfer_layer_message(char *message) { if(sm[1]==0) { // remove preset pcm_unload_preset(sm[0]); } else { - amy_execute_deltas(); + // A queued reset must land before the load, or amy_reset_oscs() + // unloads the new sample when it plays (pcm_unload_all_presets). + // Only the flush: this runs on the sending thread, and advancing + // the sequencer here would race the render thread's own tick. + amy_settle_deltas(); int16_t * ram = pcm_load(sm[0], sm[1], sm[2], 1, midinote, sm[4], sm[5]); start_receiving_transfer(sm[1]*2, (uint8_t*)ram); }