Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions src/amy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -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)

}
Expand Down
1 change: 1 addition & 0 deletions src/amy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) ;
Expand Down
6 changes: 5 additions & 1 deletion src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Loading