From 324f38676a50aa65ea04894810049f8fe8155c70 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 31 Dec 2025 10:40:13 +0100 Subject: [PATCH 01/12] pre-run plugins with 0 samples right after activation Signed-off-by: falkTX --- src/effects.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 142 insertions(+), 9 deletions(-) diff --git a/src/effects.c b/src/effects.c index 3fbd095d..334d3005 100644 --- a/src/effects.c +++ b/src/effects.c @@ -911,6 +911,7 @@ static void* PostPonedEventsThread(void* arg); #ifdef MOD_HMI_CONTROL_ENABLED static void* HMIClientThread(void* arg); #endif +static void PreRunPlugin(effect_t *effect); static int ProcessPlugin(jack_nframes_t nframes, void *arg); static bool SetPortValue(port_t *port, float value, int effect_id, bool is_bypass, bool from_ui); static float UpdateValueFromMidi(midi_cc_t* mcc, uint16_t mvalue, bool highres); @@ -1137,7 +1138,10 @@ static int BufferSize(jack_nframes_t nframes, void* data) effect->options_interface->set(effect->lilv_instance->lv2_handle, options); if (effect->activated) + { lilv_instance_activate(effect->lilv_instance); + PreRunPlugin(effect); + } } } #ifdef HAVE_HYLIA @@ -1897,6 +1901,126 @@ static void* HMIClientThread(void* arg) } #endif +static void PreRunPlugin(effect_t *effect) +{ + if ((effect->hints & HINT_IS_LIVE) == 0 && + (!g_processing_enabled || ( + (effect->hints & HINT_STATE_UNSAFE) && pthread_mutex_lock(&effect->state_restore_mutex) != 0))) + { + return; + } + + /* common variables */ + port_t *port; + unsigned int i; + float value; + bool needs_post = false; + + if (effect->hints & HINT_TRANSPORT) + { + effect->transport_rolling = g_jack_rolling; + // effect->transport_frame = pos.frame; + effect->transport_bpb = g_transport_bpb; + effect->transport_bpm = g_transport_bpm; + } + if (effect->bpb_index >= 0) + { + *(effect->ports[effect->bpb_index]->buffer) = g_transport_bpb; + } + if (effect->bpm_index >= 0) + { + *(effect->ports[effect->bpm_index]->buffer) = g_transport_bpm; + } + if (effect->speed_index >= 0) + { + *(effect->ports[effect->speed_index]->buffer) = g_jack_rolling ? 1.0f : 0.0f; + } + + /* Prepare midi/event ports */ + for (i = 0; i < effect->input_event_ports_count; i++) + { + port = effect->input_event_ports[i]; + lv2_evbuf_reset(port->evbuf, true); + } + + for (i = 0; i < effect->output_event_ports_count; i++) + lv2_evbuf_reset(effect->output_event_ports[i]->evbuf, false); + + /* Bypass */ + if (effect->bypass > 0.5f && effect->enabled_index < 0) + { + lilv_instance_run(effect->lilv_instance, 0); + } + /* Effect process */ + else + { + /* Run the effect */ + lilv_instance_run(effect->lilv_instance, 0); + + /* Notify the plugin the run() cycle is finished */ + if (effect->worker.iface) + { + /* Process any replies from the worker. */ + worker_emit_responses(&effect->worker); + if (effect->worker.iface->end_run) + { + effect->worker.iface->end_run(effect->lilv_instance->lv2_handle); + } + } + } + + if (effect->hints & HINT_TRIGGERS) + { + for (i = 0; i < effect->input_control_ports_count; i++) + { + port = effect->input_control_ports[i]; + + if ((port->hints & HINT_TRIGGER) && floats_differ_enough(port->prev_value, port->def_value)) + port->prev_value = *(port->buffer) = port->def_value; + } + } + + if (effect->hints & HINT_OUTPUT_MONITORS) + { + for (i = 0; i < effect->output_control_ports_count; i++) + { + port = effect->output_control_ports[i]; + + if ((port->hints & HINT_MONITORED) == 0) + continue; + + value = *(port->buffer); + + if (! floats_differ_enough(port->prev_value, value)) + continue; + + postponed_event_list_data* const posteventptr = rtsafe_memory_pool_allocate_atomic(g_rtsafe_mem_pool); + + if (posteventptr == NULL) + continue; + + port->prev_value = value; + + posteventptr->event.type = POSTPONED_OUTPUT_MONITOR; + posteventptr->event.parameter.effect_id = effect->instance; + posteventptr->event.parameter.symbol = port->symbol; + posteventptr->event.parameter.value = value; + + pthread_mutex_lock(&g_rtsafe_mutex); + list_add_tail(&posteventptr->siblings, &g_rtsafe_list); + pthread_mutex_unlock(&g_rtsafe_mutex); + + needs_post = true; + } + } + + if (effect->hints & HINT_STATE_UNSAFE) + pthread_mutex_unlock(&effect->state_restore_mutex); + + if (needs_post) + sem_post(&g_postevents_semaphore); +} + static int ProcessPlugin(jack_nframes_t nframes, void *arg) { effect_t *effect; @@ -5122,7 +5246,7 @@ int effects_add(const char *uri, int instance, int activate) if (lilv_plugin_has_extension_data(effect->lilv_plugin, g_lilv_nodes.worker_interface)) { const LV2_Worker_Interface *worker_interface = - (const LV2_Worker_Interface*) lilv_instance_get_extension_data(effect->lilv_instance, + (const LV2_Worker_Interface*) lilv_instance_get_extension_data(lilv_instance, LV2_WORKER__interface); worker_init(&effect->worker, lilv_instance, worker_interface, worker_buf_size); @@ -5131,21 +5255,21 @@ int effects_add(const char *uri, int instance, int activate) if (lilv_plugin_has_extension_data(effect->lilv_plugin, g_lilv_nodes.options_interface)) { effect->options_interface = - (const LV2_Options_Interface*) lilv_instance_get_extension_data(effect->lilv_instance, + (const LV2_Options_Interface*) lilv_instance_get_extension_data(lilv_instance, LV2_OPTIONS__interface); } if (lilv_plugin_has_extension_data(effect->lilv_plugin, g_lilv_nodes.license_interface)) { effect->license_iface = - (const MOD_License_Interface*) lilv_instance_get_extension_data(effect->lilv_instance, + (const MOD_License_Interface*) lilv_instance_get_extension_data(lilv_instance, MOD_LICENSE__interface); } if (lilv_plugin_has_extension_data(effect->lilv_plugin, g_lilv_nodes.state_interface)) { effect->state_iface = - (const LV2_State_Interface*) lilv_instance_get_extension_data(effect->lilv_instance, + (const LV2_State_Interface*) lilv_instance_get_extension_data(lilv_instance, LV2_STATE__interface); effect->hints |= HINT_HAS_STATE; @@ -5160,7 +5284,7 @@ int effects_add(const char *uri, int instance, int activate) LilvState *state = lilv_state_new_from_world(g_lv2_data, &g_urid_map, plugin_uri); if (state != NULL) { - lilv_state_restore(state, effect->lilv_instance, NULL, NULL, + lilv_state_restore(state, lilv_instance, NULL, NULL, LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE, effect->features); lilv_state_free(state); } @@ -5171,7 +5295,7 @@ int effects_add(const char *uri, int instance, int activate) if (lilv_plugin_has_extension_data(effect->lilv_plugin, g_lilv_nodes.hmi_interface)) { effect->hmi_notif = - (const LV2_HMI_PluginNotification*) lilv_instance_get_extension_data(effect->lilv_instance, + (const LV2_HMI_PluginNotification*) lilv_instance_get_extension_data(lilv_instance, LV2_HMI__PluginNotification); } #endif @@ -5890,6 +6014,7 @@ int effects_add(const char *uri, int instance, int activate) if (activate) { lilv_instance_activate(lilv_instance); + PreRunPlugin(effect); /* Try activate the Jack client */ if (jack_activate(jack_client) != 0) @@ -6586,6 +6711,7 @@ int effects_activate(int effect_id, int value) { effect->activated = true; lilv_instance_activate(effect->lilv_instance); + PreRunPlugin(effect); if (jack_activate(effect->jack_client) != 0) { @@ -6615,9 +6741,11 @@ int effects_activate(int effect_id, int value) static void* effects_activate_thread(void* arg) { - jack_client_t *jack_client = arg; + effect_t *effect = arg; - if (jack_activate(jack_client) != 0) + PreRunPlugin(effect); + + if (jack_activate(effect->jack_client) != 0) fprintf(stderr, "can't activate jack_client\n"); return NULL; @@ -6666,10 +6794,15 @@ int effects_activate_multi(int value, int num_effects, int *effects) effect->activated = true; lilv_instance_activate(effect->lilv_instance); - if (zix_thread_create(&threads[num_threads], sizeof(void*), effects_activate_thread, effect->jack_client) == 0) + if (zix_thread_create(&threads[num_threads], sizeof(void*), effects_activate_thread, effect) == 0) + { ++num_threads; + } else + { + PreRunPlugin(effect); jack_activate(effect->jack_client); + } } } else From 48a8584e64ffdcb2094eba34672b930751eb3efb Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 8 Jan 2026 20:51:53 +0100 Subject: [PATCH 02/12] Schedule multi param changes so they happen in sync Signed-off-by: falkTX --- src/effects.c | 154 +++++++++++++++++++++++++++-------- src/effects.h | 3 + src/monitor/monitor-client.c | 8 ++ 3 files changed, 130 insertions(+), 35 deletions(-) diff --git a/src/effects.c b/src/effects.c index 334d3005..b64a3e9e 100644 --- a/src/effects.c +++ b/src/effects.c @@ -611,6 +611,11 @@ typedef struct HMI_ADDRESSING_T { } hmi_addressing_t; #endif +typedef struct SYNC_SCHEDULED_PARAM_T { + port_t *port; + float value; +} sync_scheduled_param_t; + typedef struct POSTPONED_PARAMETER_EVENT_T { int effect_id; const char* symbol; @@ -725,6 +730,7 @@ typedef struct RAW_MIDI_PORT_ITEM { typedef struct CACHED_EFFECT_FLUSH_T { effect_t *effect; port_t **ports; + int num_ports; } cached_effect_flush_t; @@ -861,6 +867,11 @@ static pthread_mutex_t g_midi_learning_mutex; static bool g_monitored_midi_controls[16]; static bool g_monitored_midi_programs[16]; +/* Postponed port updates */ +static sync_scheduled_param_t g_sync_scheduled_params[MAX_SYNC_SCHEDULED_PARAMS]; +static pthread_mutex_t g_sync_scheduled_params_mutex; +static unsigned int g_sync_scheduled_param_count; + #ifdef HAVE_HYLIA static hylia_t* g_hylia_instance; static hylia_time_info_t g_hylia_timeinfo; @@ -4498,6 +4509,7 @@ int effects_init(void* client) pthread_mutex_init(&g_raw_midi_port_mutex, &mutex_atts); pthread_mutex_init(&g_audio_monitor_mutex, &mutex_atts); pthread_mutex_init(&g_midi_learning_mutex, &mutex_atts); + pthread_mutex_init(&g_sync_scheduled_params_mutex, &mutex_atts); #ifdef MOD_HMI_CONTROL_ENABLED pthread_mutex_init(&g_hmi_mutex, &mutex_atts); #endif @@ -5084,6 +5096,7 @@ int effects_finish(int close_client) pthread_mutex_destroy(&g_raw_midi_port_mutex); pthread_mutex_destroy(&g_audio_monitor_mutex); pthread_mutex_destroy(&g_midi_learning_mutex); + pthread_mutex_destroy(&g_sync_scheduled_params_mutex); #ifdef MOD_HMI_CONTROL_ENABLED pthread_mutex_destroy(&g_hmi_mutex); #endif @@ -6561,11 +6574,19 @@ int effects_remove(int effect_id) start = 0; end = MAX_PLUGIN_INSTANCES; + + // clear all sync scheduled params if removing all plugins + pthread_mutex_lock(&g_sync_scheduled_params_mutex); + g_sync_scheduled_param_count = 0; + pthread_mutex_unlock(&g_sync_scheduled_params_mutex); } else { start = effect_id; end = start + 1; + + // trigger sync scheduled params now for no dangling pointers + effect_sync_scheduled_params(false); } // stop plugins processing @@ -7000,17 +7021,36 @@ int effects_set_parameter_multi(const char *control_symbol, float value, int num } // the critical loop, must be as fast and small as possible - port_t *port; - for (int i = 0; i < num_ports; i++) + bool scheduled = true; + pthread_mutex_lock(&g_sync_scheduled_params_mutex); + if (g_sync_scheduled_param_count + num_ports <= MAX_SYNC_SCHEDULED_PARAMS) { - port = ports[i]; - port->prev_value = *port->buffer = value; + int p = g_sync_scheduled_param_count; + for (int i = 0; i < num_ports; i++, p++) + { + g_sync_scheduled_params[p].port = ports[i]; + g_sync_scheduled_params[p].value = value; + } + g_sync_scheduled_param_count = p; } + else + { + scheduled = false; + } + pthread_mutex_unlock(&g_sync_scheduled_params_mutex); + if (! scheduled) + { + // no space to schedule events, trigger param changes now + for (int i = 0; i < num_ports; i++) + { + port_t *port = ports[i]; + port->prev_value = *port->buffer = value; #ifdef WITH_EXTERNAL_UI_SUPPORT - for (int i = 0; i < num_ports; i++) - ports[i]->hints |= HINT_SHOULD_UPDATE; + port->hints |= HINT_SHOULD_UPDATE; #endif + } + } free(ports); @@ -7080,6 +7120,7 @@ int effects_flush_parameters_multi(int reset, int param_count, const flushed_par if (num_effects == 1) return effects_flush_parameters(*effects, reset, param_count, params); + cached_effect_flush_t *cached_effect; effect_t *effect; port_t *port; @@ -7096,54 +7137,70 @@ int effects_flush_parameters_multi(int reset, int param_count, const flushed_par { effect = &(g_effects[effect_id]); - cached_effect_flush_t *cached_effect = &cached_effects[num_cached_effects++]; + cached_effect = &cached_effects[num_cached_effects++]; cached_effect->effect = effect; cached_effect->ports = malloc(sizeof(port_t*) * param_count); + cached_effect->num_ports = 0; for (int j = 0; j < param_count; j++) - cached_effect->ports[j] = FindEffectInputPortBySymbol(effect, params[j].symbol); + { + if ((cached_effect->ports[cached_effect->num_ports] = FindEffectInputPortBySymbol(effect, params[j].symbol))) + ++cached_effect->num_ports; + } } } - for (int i = 0; i < num_cached_effects; i++) + // the critical loop, must be as fast and small as possible + bool scheduled = true; + const int resetb = reset != 0 ? 1 : 0; + pthread_mutex_lock(&g_sync_scheduled_params_mutex); + if (g_sync_scheduled_param_count + (param_count + resetb) * num_cached_effects <= MAX_SYNC_SCHEDULED_PARAMS) { - effect = cached_effects[i].effect; - if (effect->reset_index >= 0 && reset != 0) + int p = g_sync_scheduled_param_count; + for (int i = 0; i < num_cached_effects; i++) { - port = effect->ports[effect->reset_index]; - port->prev_value = *(port->buffer) = reset; + cached_effect = &cached_effects[i]; + effect = cached_effect->effect; + for (int j = 0; j < cached_effect->num_ports; j++, p++) + { + g_sync_scheduled_params[p].port = cached_effect->ports[j]; + g_sync_scheduled_params[p].value = params[j].value; + } + if (effect->reset_index >= 0 && reset != 0) + { + g_sync_scheduled_params[p].port = effect->ports[effect->reset_index]; + g_sync_scheduled_params[p].value = reset; + ++p; + } } + g_sync_scheduled_param_count = p; + } + else + { + scheduled = false; } + pthread_mutex_unlock(&g_sync_scheduled_params_mutex); - // the critical loop, must be as fast and small as possible - for (int j = 0; j < param_count; j++) + if (! scheduled) { + // no space to schedule events, trigger param changes now for (int i = 0; i < num_cached_effects; i++) { - if ((port = cached_effects[i].ports[j])) + cached_effect = &cached_effects[i]; + effect = cached_effect->effect; + for (int j = 0; j < cached_effect->num_ports; j++) + { + port = cached_effect->ports[j]; port->prev_value = *(port->buffer) = params[j].value; - } - } - #ifdef WITH_EXTERNAL_UI_SUPPORT - for (int i = 0; i < num_cached_effects; i++) - { - for (int j = 0; j < param_count; j++) - { - if ((port = cached_effects[i].ports[j])) port->hints |= HINT_SHOULD_UPDATE; - } - } #endif - - // reset a 2nd time in case plugin was processing while we changed parameters - for (int i = 0; i < num_cached_effects; i++) - { - effect = cached_effects[i].effect; - if (effect->reset_index >= 0 && reset != 0) - { - port = effect->ports[effect->reset_index]; - port->prev_value = *(port->buffer) = reset; + } + if (effect->reset_index >= 0 && reset != 0) + { + port = effect->ports[effect->reset_index]; + port->prev_value = *(port->buffer) = reset; + } } } @@ -9454,6 +9511,33 @@ int effects_transport_sync_mode(const char* mode) return SUCCESS; } +void effect_sync_scheduled_params(int realtime) +{ + port_t *port; + + if (realtime != 0) + { + if (pthread_mutex_trylock(&g_sync_scheduled_params_mutex) != 0) + return; + } + else + { + pthread_mutex_lock(&g_sync_scheduled_params_mutex); + } + + for (unsigned int i = 0; i < g_sync_scheduled_param_count; i++) + { + port = g_sync_scheduled_params[i].port; + port->prev_value = *port->buffer = g_sync_scheduled_params[i].value; +#ifdef WITH_EXTERNAL_UI_SUPPORT + port->hints |= HINT_SHOULD_UPDATE; +#endif + } + + g_sync_scheduled_param_count = 0; + pthread_mutex_unlock(&g_sync_scheduled_params_mutex); +} + void effects_output_data_ready(void) { if (g_verbose_debug) { diff --git a/src/effects.h b/src/effects.h index 73a4f7c6..80b08d5a 100644 --- a/src/effects.h +++ b/src/effects.h @@ -97,6 +97,8 @@ typedef enum { #define MAX_POSTPONED_EVENTS 8192 #define MAX_HMI_ADDRESSINGS 128 +#define MAX_SYNC_SCHEDULED_PARAMS 512 + // used for local stack variables #define MAX_CHAR_BUF_SIZE 255 @@ -203,6 +205,7 @@ int effects_monitor_midi_control(int channel, int enable); int effects_monitor_midi_program(int channel, int enable); void effects_transport(int rolling, double beats_per_bar, double beats_per_minute); int effects_transport_sync_mode(const char *mode); +void effect_sync_scheduled_params(int realtime); void effects_output_data_ready(void); int effects_show_external_ui(int effect_id); void effects_idle_external_uis(void); diff --git a/src/monitor/monitor-client.c b/src/monitor/monitor-client.c index 8e832c36..b8153cde 100644 --- a/src/monitor/monitor-client.c +++ b/src/monitor/monitor-client.c @@ -35,6 +35,10 @@ #include "../utils.h" #include "../dsp/compressor_core.h" +#ifndef STANDALONE_MONITOR_CLIENT +#include "../effects.h" +#endif + /* ************************************************************************************************************************ * LOCAL DEFINES @@ -283,6 +287,10 @@ static int ProcessMonitor(jack_nframes_t nframes, void *arg) { monitor_client_t *const mon = arg; + #ifndef STANDALONE_MONITOR_CLIENT + effect_sync_scheduled_params(true); + #endif + if (mon->muted) { for (uint32_t i=0; i < mon->numports; ++i) From 37cd7f800613cb7354d20912bc2e0aa1968b8cb5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 26 Jan 2026 12:59:33 +0100 Subject: [PATCH 03/12] Always pre-run effect, even if processing is globally disabled Signed-off-by: falkTX --- src/effects.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/effects.c b/src/effects.c index b64a3e9e..c76f01a5 100644 --- a/src/effects.c +++ b/src/effects.c @@ -1914,12 +1914,8 @@ static void* HMIClientThread(void* arg) static void PreRunPlugin(effect_t *effect) { - if ((effect->hints & HINT_IS_LIVE) == 0 && - (!g_processing_enabled || ( - (effect->hints & HINT_STATE_UNSAFE) && pthread_mutex_lock(&effect->state_restore_mutex) != 0))) - { - return; - } + if (effect->hints & HINT_STATE_UNSAFE) + pthread_mutex_lock(&effect->state_restore_mutex); /* common variables */ port_t *port; From 30f94294f4ae5bb5eacdcb7ac30e36a95f30f319 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 13 Apr 2026 17:50:01 +0200 Subject: [PATCH 04/12] dont pre-run automatically Signed-off-by: falkTX --- src/effects.c | 47 ++++++++++++----------------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/src/effects.c b/src/effects.c index 1a4f1c92..bd6772d6 100644 --- a/src/effects.c +++ b/src/effects.c @@ -1153,10 +1153,7 @@ static int BufferSize(jack_nframes_t nframes, void* data) effect->options_interface->set(effect->lilv_instance->lv2_handle, options); if (effect->activated) - { lilv_instance_activate(effect->lilv_instance); - PreRunPlugin(effect); - } } } #ifdef HAVE_HYLIA @@ -6031,7 +6028,6 @@ int effects_add(const char *uri, int instance, int activate) if (activate) { lilv_instance_activate(lilv_instance); - PreRunPlugin(effect); /* Try activate the Jack client */ if (jack_activate(jack_client) != 0) @@ -6736,7 +6732,6 @@ int effects_activate(int effect_id, int value) { effect->activated = true; lilv_instance_activate(effect->lilv_instance); - PreRunPlugin(effect); if (jack_activate(effect->jack_client) != 0) { @@ -6768,7 +6763,9 @@ static void* effects_activate_thread(void* arg) { effect_t *effect = arg; - PreRunPlugin(effect); + effect->activated = true; + + lilv_instance_activate(effect->lilv_instance); if (jack_activate(effect->jack_client) != 0) fprintf(stderr, "can't activate jack_client\n"); @@ -6778,11 +6775,15 @@ static void* effects_activate_thread(void* arg) static void* effects_deactivate_thread(void* arg) { - jack_client_t *jack_client = arg; + effect_t *effect = arg; - if (jack_deactivate(jack_client) != 0) + if (jack_deactivate(effect->jack_client) != 0) fprintf(stderr, "can't deactivate jack_client\n"); + lilv_instance_deactivate(effect->lilv_instance); + + effect->activated = false; + return NULL; } @@ -6803,7 +6804,7 @@ int effects_activate_multi(int value, int num_effects, int *effects) effect_t *effect; // create threads to activate all clients - // reduces time spent waiting for jack operations to complete, triggering in parallel + // reduces time spent waiting for plugin and jack operations to complete, triggering in parallel for (int i = 0, effect_id; i < num_effects; ++i) { effect_id = effects[i]; @@ -6816,18 +6817,10 @@ int effects_activate_multi(int value, int num_effects, int *effects) { if (! effect->activated) { - effect->activated = true; - lilv_instance_activate(effect->lilv_instance); - if (zix_thread_create(&threads[num_threads], sizeof(void*), effects_activate_thread, effect) == 0) - { ++num_threads; - } else - { - PreRunPlugin(effect); - jack_activate(effect->jack_client); - } + effects_activate_thread(effect); } } else @@ -6837,7 +6830,7 @@ int effects_activate_multi(int value, int num_effects, int *effects) if (zix_thread_create(&threads[num_threads], sizeof(void*), effects_deactivate_thread, effect->jack_client) == 0) ++num_threads; else - jack_deactivate(effect->jack_client); + effects_deactivate_thread(effect); } } } @@ -6848,22 +6841,6 @@ int effects_activate_multi(int value, int num_effects, int *effects) free(threads); - // if deactivating, do the last lv2 deactivate step now - if (! value) - { - for (int i = 0, effect_id; i < num_effects; ++i) - { - effect_id = effects[i]; - effect = &g_effects[effect_id]; - - if (! effect->activated || effect->jack_client == NULL) - continue; - - effect->activated = false; - lilv_instance_deactivate(effect->lilv_instance); - } - } - return SUCCESS; } From 28017989e88749e585c617d8cc54214a1522b540 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 13 Apr 2026 18:15:29 +0200 Subject: [PATCH 05/12] Use new multi_pre_run command as the trigger for pre-runs Signed-off-by: falkTX --- README.md | 7 +++++++ src/effects.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ src/effects.h | 1 + src/mod-host.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/mod-host.h | 1 + 5 files changed, 116 insertions(+) diff --git a/README.md b/README.md index f124cd53..1c848789 100644 --- a/README.md +++ b/README.md @@ -341,8 +341,15 @@ The commands supported by mod-host are: multi_params_flush * flush several param values at once and trigger reset if available (multiple instance variant) * reset value must be according to reset property spec + * all instances must be in activated state e.g.: multi_params_flush 1 2 0 1 2 "gain" 0.0 "distortion" 0.5 + multi_pre_run + * pre-run and flush several param values at once and trigger reset if available (multiple instance variant) + * reset value must be according to reset property spec + * all instances must be in deactivated state + e.g.: multi_pre_run 1 2 0 1 2 "gain" 0.0 "distortion" 0.5 + help * show a help message diff --git a/src/effects.c b/src/effects.c index bd6772d6..62976c32 100644 --- a/src/effects.c +++ b/src/effects.c @@ -7118,6 +7118,12 @@ int effects_flush_parameters_multi(int reset, int param_count, const flushed_par { effect = &(g_effects[effect_id]); + if (! effect->activated) + { + fprintf(stderr, "multi-param-flush attempted on non-activated plugin #%d\n", effect->instance); + continue; + } + cached_effect = &cached_effects[num_cached_effects++]; cached_effect->effect = effect; cached_effect->ports = malloc(sizeof(port_t*) * param_count); @@ -7193,6 +7199,52 @@ int effects_flush_parameters_multi(int reset, int param_count, const flushed_par return SUCCESS; } +int effects_pre_run_multi(int reset, int param_count, const flushed_param_t *params, int num_effects, int *effects) +{ + if (num_effects <= 0) + return ERR_INVALID_OPERATION; + + effect_t *effect; + port_t *port; + + for (int i = 0, effect_id; i < num_effects; i++) + { + effect_id = effects[i]; + if (InstanceExist(effect_id)) + { + effect = &(g_effects[effect_id]); + + if (effect->activated) + { + fprintf(stderr, "pre-run attempted on activated plugin #%d\n", effect->instance); + continue; + } + + for (int j = 0; j < param_count; j++) + { + port = FindEffectInputPortBySymbol(effect, params[j].symbol); + + if (port) + { + port->prev_value = *(port->buffer) = params[j].value; +#ifdef WITH_EXTERNAL_UI_SUPPORT + port->hints |= HINT_SHOULD_UPDATE; +#endif + } + if (effect->reset_index >= 0 && reset != 0) + { + port = effect->ports[effect->reset_index]; + port->prev_value = *(port->buffer) = reset; + } + } + + PreRunPlugin(effect); + } + } + + return SUCCESS; +} + static inline bool lv2_atom_forge_property_set(LV2_Atom_Forge *forge, LV2_URID urid, const char *value, LV2_URID type) { diff --git a/src/effects.h b/src/effects.h index 80b08d5a..d08c1253 100644 --- a/src/effects.h +++ b/src/effects.h @@ -160,6 +160,7 @@ int effects_set_parameter_multi(const char *control_symbol, float value, int num int effects_get_parameter(int effect_id, const char *control_symbol, float *value); int effects_flush_parameters(int effect_id, int reset, int param_count, const flushed_param_t *params); int effects_flush_parameters_multi(int reset, int param_count, const flushed_param_t *params, int num_effects, int *effects); +int effects_pre_run_multi(int reset, int param_count, const flushed_param_t *params, int num_effects, int *effects); int effects_set_property(int effect_id, const char *uri, const char *value); int effects_get_property(int effect_id, const char *uri); int effects_monitor_parameter(int effect_id, const char *control_symbol, const char *op, float value); diff --git a/src/mod-host.c b/src/mod-host.c index f9c9b99d..9c6b16a3 100644 --- a/src/mod-host.c +++ b/src/mod-host.c @@ -924,6 +924,60 @@ static void multi_params_flush(proto_t *proto) free(params); } +static void multi_pre_run(proto_t *proto) +{ + int instance_count = atoi(proto->list[2]); + if (instance_count == 0) + { + protocol_response_int(ERR_ASSIGNMENT_INVALID_OP, proto); + return; + } + + int *instances = malloc(sizeof(int) * instance_count); + + if (instances != NULL) + { + for (int i = 0; i < instance_count; i++) + instances[i] = atoi(proto->list[3 + i]); + } + else + { + protocol_response_int(ERR_MEMORY_ALLOCATION, proto); + return; + } + + int param_count = atoi(proto->list[3 + instance_count]); + flushed_param_t *params; + + if (param_count != 0) + { + params = malloc(sizeof(flushed_param_t) * param_count); + + if (params == NULL) + { + free(instances); + protocol_response_int(ERR_MEMORY_ALLOCATION, proto); + return; + } + + for (int i = 0; i < param_count; i++) + { + params[i].symbol = proto->list[4 + instance_count + i * 2]; + params[i].value = atof(proto->list[5 + instance_count + i * 2]); + } + } + else + { + params = NULL; + } + + int resp = effects_pre_run_multi(atoi(proto->list[1]), param_count, params, instance_count, instances); + protocol_response_int(resp, proto); + + free(instances); + free(params); +} + static void help_cb(proto_t *proto) { proto->response = 0; @@ -1097,6 +1151,7 @@ static int mod_host_init(jack_client_t* client, int socket_port, int feedback_po protocol_add_command(MULTI_BYPASS, multi_bypass); protocol_add_command(MULTI_PARAM_SET, multi_param_set); protocol_add_command(MULTI_PARAMS_FLUSH, multi_params_flush); + protocol_add_command(MULTI_PRE_RUN, multi_pre_run); /* skip help and quit for internal client */ if (client == NULL) diff --git a/src/mod-host.h b/src/mod-host.h index 6778f7ce..0b788904 100644 --- a/src/mod-host.h +++ b/src/mod-host.h @@ -111,6 +111,7 @@ #define MULTI_BYPASS "multi_bypass %i %i ..." #define MULTI_PARAM_SET "multi_param_set %s %f %i ..." #define MULTI_PARAMS_FLUSH "multi_params_flush %i %i ... %i ..." +#define MULTI_PRE_RUN "multi_pre_run %i %i ... %i ..." #define HELP "help" #define QUIT "quit" From 979dd893310f38dbe8a2afac3ca64243df4072ed Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 13 Apr 2026 18:25:29 +0200 Subject: [PATCH 06/12] Fix wording Signed-off-by: falkTX --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c848789..e7f138f8 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ The commands supported by mod-host are: e.g.: multi_params_flush 1 2 0 1 2 "gain" 0.0 "distortion" 0.5 multi_pre_run - * pre-run and flush several param values at once and trigger reset if available (multiple instance variant) + * pre-run and flush several param values at once and trigger reset if available (for multiple instance) * reset value must be according to reset property spec * all instances must be in deactivated state e.g.: multi_pre_run 1 2 0 1 2 "gain" 0.0 "distortion" 0.5 From 14815fd2358a09c3bc44ff32673a391dddf21e87 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 14 Apr 2026 14:08:13 +0200 Subject: [PATCH 07/12] Fix last commit, pass the correct arg to thread Signed-off-by: falkTX --- src/effects.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/effects.c b/src/effects.c index 62976c32..8baa8838 100644 --- a/src/effects.c +++ b/src/effects.c @@ -6668,10 +6668,10 @@ int effects_remove_multi(int num_effects, int *effects) if (effect->activated) { - if (zix_thread_create(&threads[num_threads], sizeof(void*), effects_deactivate_thread, effect->jack_client) == 0) + if (zix_thread_create(&threads[num_threads], sizeof(void*), effects_deactivate_thread, effect) == 0) ++num_threads; else - jack_deactivate(effect->jack_client); + effects_deactivate_thread(effect); } } @@ -6827,7 +6827,7 @@ int effects_activate_multi(int value, int num_effects, int *effects) { if (effect->activated) { - if (zix_thread_create(&threads[num_threads], sizeof(void*), effects_deactivate_thread, effect->jack_client) == 0) + if (zix_thread_create(&threads[num_threads], sizeof(void*), effects_deactivate_thread, effect) == 0) ++num_threads; else effects_deactivate_thread(effect); From 09645a8c4958878213f31da06a2e9dfe2c73b204 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 14 Apr 2026 15:44:23 +0200 Subject: [PATCH 08/12] Add single pre_run command Signed-off-by: falkTX --- README.md | 8 +++++++- src/effects.c | 43 ++++++++++++++++++++++++++++++++++++++++++- src/effects.h | 1 + src/mod-host.c | 34 ++++++++++++++++++++++++++++++++++ src/mod-host.h | 1 + 5 files changed, 85 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e7f138f8..d3526b71 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,12 @@ The commands supported by mod-host are: * reset value must be according to reset property spec e.g.: params_flush 0 1 2 "gain" 0.0 "distortion" 0.5 + pre_run + * pre-run and flush several param values at once and trigger reset if available + * reset value must be according to reset property spec + * instance must be in deactivated state + e.g.: pre_run 0 1 2 "gain" 0.0 "distortion" 0.5 + patch_set * set the value of a control port e.g.: patch_set 0 "gain" 2.5 @@ -345,7 +351,7 @@ The commands supported by mod-host are: e.g.: multi_params_flush 1 2 0 1 2 "gain" 0.0 "distortion" 0.5 multi_pre_run - * pre-run and flush several param values at once and trigger reset if available (for multiple instance) + * pre-run and flush several param values at once and trigger reset if available (multiple instance variant) * reset value must be according to reset property spec * all instances must be in deactivated state e.g.: multi_pre_run 1 2 0 1 2 "gain" 0.0 "distortion" 0.5 diff --git a/src/effects.c b/src/effects.c index 8baa8838..5857ee9a 100644 --- a/src/effects.c +++ b/src/effects.c @@ -7199,11 +7199,52 @@ int effects_flush_parameters_multi(int reset, int param_count, const flushed_par return SUCCESS; } +int effects_pre_run(int effect_id, int reset, int param_count, const flushed_param_t *params) +{ + if (!InstanceExist(effect_id)) + return ERR_INSTANCE_NON_EXISTS; + + effect_t *effect = &(g_effects[effect_id]); + + if (effect->activated) + { + fprintf(stderr, "pre-run attempted on activated plugin #%d\n", effect_id); + return ERR_INVALID_OPERATION; + } + + port_t *port; + + for (int i = 0; i < param_count; i++) + { + port = FindEffectInputPortBySymbol(effect, params[i].symbol); + if (port) + { + port->prev_value = *(port->buffer) = params[i].value; +#ifdef WITH_EXTERNAL_UI_SUPPORT + port->hints |= HINT_SHOULD_UPDATE; +#endif + } + } + + if (effect->reset_index >= 0 && reset != 0) + { + port = effect->ports[effect->reset_index]; + port->prev_value = *(port->buffer) = reset; + } + + PreRunPlugin(effect); + + return SUCCESS; +} + int effects_pre_run_multi(int reset, int param_count, const flushed_param_t *params, int num_effects, int *effects) { if (num_effects <= 0) return ERR_INVALID_OPERATION; + if (num_effects == 1) + return effects_pre_run(*effects, reset, param_count, params); + effect_t *effect; port_t *port; @@ -7216,7 +7257,7 @@ int effects_pre_run_multi(int reset, int param_count, const flushed_param_t *par if (effect->activated) { - fprintf(stderr, "pre-run attempted on activated plugin #%d\n", effect->instance); + fprintf(stderr, "multi-pre-run attempted on activated plugin #%d\n", effect->instance); continue; } diff --git a/src/effects.h b/src/effects.h index d08c1253..d2663767 100644 --- a/src/effects.h +++ b/src/effects.h @@ -160,6 +160,7 @@ int effects_set_parameter_multi(const char *control_symbol, float value, int num int effects_get_parameter(int effect_id, const char *control_symbol, float *value); int effects_flush_parameters(int effect_id, int reset, int param_count, const flushed_param_t *params); int effects_flush_parameters_multi(int reset, int param_count, const flushed_param_t *params, int num_effects, int *effects); +int effects_pre_run(int effect_id, int reset, int param_count, const flushed_param_t *params); int effects_pre_run_multi(int reset, int param_count, const flushed_param_t *params, int num_effects, int *effects); int effects_set_property(int effect_id, const char *uri, const char *value); int effects_get_property(int effect_id, const char *uri); diff --git a/src/mod-host.c b/src/mod-host.c index 9c6b16a3..36d964a8 100644 --- a/src/mod-host.c +++ b/src/mod-host.c @@ -312,6 +312,39 @@ static void effects_flush_params_cb(proto_t *proto) free(params); } +static void effects_pre_run_cb(proto_t *proto) +{ + int resp; + int param_count = atoi(proto->list[3]); + flushed_param_t *params; + + if (param_count != 0) + { + params = malloc(sizeof(flushed_param_t) * param_count); + + if (params == NULL) + { + protocol_response_int(ERR_MEMORY_ALLOCATION, proto); + return; + } + + for (int i = 0; i < param_count; i++) + { + params[i].symbol = proto->list[4 + i * 2]; + params[i].value = atof(proto->list[5 + i * 2]); + } + } + else + { + params = NULL; + } + + resp = effects_pre_run(atoi(proto->list[1]), atoi(proto->list[2]), param_count, params); + protocol_response_int(resp, proto); + + free(params); +} + static void effects_set_property_cb(proto_t *proto) { int resp; @@ -1107,6 +1140,7 @@ static int mod_host_init(jack_client_t* client, int socket_port, int feedback_po protocol_add_command(EFFECT_PARAM_GET, effects_get_param_cb); protocol_add_command(EFFECT_PARAM_MON, effects_monitor_param_cb); protocol_add_command(EFFECT_PARAMS_FLUSH, effects_flush_params_cb); + protocol_add_command(EFFECT_PRE_RUN, effects_pre_run_cb); protocol_add_command(EFFECT_PATCH_GET, effects_get_property_cb); protocol_add_command(EFFECT_PATCH_SET, effects_set_property_cb); protocol_add_command(EFFECT_LICENSEE, effects_licensee_cb); diff --git a/src/mod-host.h b/src/mod-host.h index 0b788904..7e9771f8 100644 --- a/src/mod-host.h +++ b/src/mod-host.h @@ -69,6 +69,7 @@ #define EFFECT_PARAM_GET "param_get %i %s" #define EFFECT_PARAM_MON "param_monitor %i %s %s %f" #define EFFECT_PARAMS_FLUSH "params_flush %i %i %i ..." +#define EFFECT_PRE_RUN "pre_run %i %i %i ..." #define EFFECT_PATCH_GET "patch_get %i %s" #define EFFECT_PATCH_SET "patch_set %i %s %s" #define EFFECT_LICENSEE "licensee %i" From 74c3a21ddb505030d68218064118605bbc9a1635 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 16 Apr 2026 11:06:56 +0200 Subject: [PATCH 09/12] Wait for process to stop when disabling effects processing Signed-off-by: falkTX --- src/effects.c | 17 +++++++++++++---- src/monitor/monitor-client.c | 33 +++++++++++++++++++++++++++++---- src/monitor/monitor-client.h | 1 + 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/effects.c b/src/effects.c index 5857ee9a..3bb039cd 100644 --- a/src/effects.c +++ b/src/effects.c @@ -803,9 +803,9 @@ static volatile double g_transport_bpm; static volatile bool g_transport_reset; static volatile enum TransportSyncMode g_transport_sync_mode; static bool g_aggregated_midi_enabled; -static bool g_processing_enabled; static bool g_verbose_debug; static bool g_cpu_load_enabled; +static volatile bool g_processing_enabled; static volatile bool g_cpu_load_trigger; // Wall clock time since program startup @@ -7206,7 +7206,7 @@ int effects_pre_run(int effect_id, int reset, int param_count, const flushed_par effect_t *effect = &(g_effects[effect_id]); - if (effect->activated) + if (effect->activated && ((effect->hints & HINT_IS_LIVE) == 0 || g_processing_enabled)) { fprintf(stderr, "pre-run attempted on activated plugin #%d\n", effect_id); return ERR_INVALID_OPERATION; @@ -7255,7 +7255,7 @@ int effects_pre_run_multi(int reset, int param_count, const flushed_param_t *par { effect = &(g_effects[effect_id]); - if (effect->activated) + if (effect->activated && ((effect->hints & HINT_IS_LIVE) == 0 || g_processing_enabled)) { fprintf(stderr, "multi-pre-run attempted on activated plugin #%d\n", effect->instance); continue; @@ -9349,7 +9349,15 @@ int effects_processing_enable(int enable) // regular on/off case 0: case 1: - g_processing_enabled = enable != 0; + if (g_processing_enabled && enable == 0) + { + g_processing_enabled = false; + monitor_client_wait_proc(); + } + else + { + g_processing_enabled = enable != 0; + } break; // turn on while reporting feedback data ready @@ -9365,6 +9373,7 @@ int effects_processing_enable(int enable) { monitor_client_wait_volume(); g_processing_enabled = false; + monitor_client_wait_proc(); } break; diff --git a/src/monitor/monitor-client.c b/src/monitor/monitor-client.c index b8153cde..0daf5a92 100644 --- a/src/monitor/monitor-client.c +++ b/src/monitor/monitor-client.c @@ -22,6 +22,7 @@ ************************************************************************************************************************ */ +#include #include #include #include @@ -72,6 +73,7 @@ typedef struct MONITOR_CLIENT_T { jack_client_t *client; jack_port_t **in_ports; jack_port_t **out_ports; + sem_t wait_proc_sem; sem_t wait_volume_sem; uint64_t connected; uint32_t numports; @@ -89,7 +91,7 @@ typedef struct MONITOR_CLIENT_T { bool apply_compressor; bool apply_volume, apply_smoothing; bool muted; - bool wait_volume; + atomic_bool wait_proc, wait_volume; } monitor_client_t; /* @@ -365,9 +367,12 @@ static int ProcessMonitor(jack_nframes_t nframes, void *arg) mon->muted = smooth_volume <= db2lin(MOD_MONITOR_VOLUME_MUTE) || ! floats_differ_enough(smooth_volume, db2lin(MOD_MONITOR_VOLUME_MUTE)); - if (mon->wait_volume && fabsf(volume - smooth_volume) < 0.000001f) + if (atomic_exchange(&mon->wait_proc, false)) + sem_post(&mon->wait_proc_sem); + + if (atomic_load(&mon->wait_volume) && fabsf(volume - smooth_volume) < 0.000001f) { - mon->wait_volume = false; + atomic_store(&mon->wait_volume, false); sem_post(&mon->wait_volume_sem); } @@ -498,6 +503,10 @@ int jack_initialize(jack_client_t* client, const char* load_init) jack_port_tie(mon->in_ports[i], mon->out_ports[i]); } + atomic_init(&mon->wait_proc, false); + atomic_init(&mon->wait_volume, false); + + sem_init(&mon->wait_proc_sem, 0, 0); sem_init(&mon->wait_volume_sem, 0, 0); /* Set jack callbacks */ @@ -508,6 +517,7 @@ int jack_initialize(jack_client_t* client, const char* load_init) if (jack_activate(client) != 0) { fprintf(stderr, "can't activate jack client\n"); + sem_destroy(&mon->wait_proc_sem); sem_destroy(&mon->wait_volume_sem); free(mon); return 1; @@ -578,6 +588,7 @@ void jack_finish(void* arg) monitor_client_t *const mon = arg; jack_deactivate(mon->client); + sem_destroy(&mon->wait_proc_sem); sem_destroy(&mon->wait_volume_sem); g_monitor_handle = NULL; @@ -724,6 +735,20 @@ bool monitor_client_flush_volume(void) return true; } +bool monitor_client_wait_proc(void) +{ + monitor_client_t *const mon = g_monitor_handle; + + if (!mon) + { + fprintf(stderr, "asked to wait for proc while monitor client is not active\n"); + return false; + } + + atomic_store(&mon->wait_proc, true); + return sem_timedwait_secs(&mon->wait_proc_sem, 1) == 0; +} + bool monitor_client_wait_volume(void) { monitor_client_t *const mon = g_monitor_handle; @@ -734,6 +759,6 @@ bool monitor_client_wait_volume(void) return false; } - mon->wait_volume = true; + atomic_store(&mon->wait_volume, true); return sem_timedwait_secs(&mon->wait_volume_sem, 1) == 0; } diff --git a/src/monitor/monitor-client.h b/src/monitor/monitor-client.h index f6fe657e..c6d706d5 100644 --- a/src/monitor/monitor-client.h +++ b/src/monitor/monitor-client.h @@ -88,6 +88,7 @@ void monitor_client_stop(void); bool monitor_client_setup_compressor(int mode, float release); bool monitor_client_setup_volume(float volume); bool monitor_client_flush_volume(void); +bool monitor_client_wait_proc(void); bool monitor_client_wait_volume(void); From b0701a2554f0fae1bf91c536c8c7d3e28ea5380b Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Apr 2026 10:12:42 +0200 Subject: [PATCH 10/12] Fix a typo Signed-off-by: falkTX --- src/effects.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/effects.c b/src/effects.c index 3bb039cd..cd18fb77 100644 --- a/src/effects.c +++ b/src/effects.c @@ -7206,7 +7206,7 @@ int effects_pre_run(int effect_id, int reset, int param_count, const flushed_par effect_t *effect = &(g_effects[effect_id]); - if (effect->activated && ((effect->hints & HINT_IS_LIVE) == 0 || g_processing_enabled)) + if (effect->activated && ((effect->hints & HINT_IS_LIVE) != 0 || g_processing_enabled)) { fprintf(stderr, "pre-run attempted on activated plugin #%d\n", effect_id); return ERR_INVALID_OPERATION; @@ -7255,7 +7255,7 @@ int effects_pre_run_multi(int reset, int param_count, const flushed_param_t *par { effect = &(g_effects[effect_id]); - if (effect->activated && ((effect->hints & HINT_IS_LIVE) == 0 || g_processing_enabled)) + if (effect->activated && ((effect->hints & HINT_IS_LIVE) != 0 || g_processing_enabled)) { fprintf(stderr, "multi-pre-run attempted on activated plugin #%d\n", effect->instance); continue; From e8941b82f606147b6870f63bd208c0a149e01f92 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Apr 2026 11:05:10 +0200 Subject: [PATCH 11/12] Add wait_audio_cycle command Signed-off-by: falkTX --- README.md | 3 +++ src/mod-host.c | 8 ++++++++ src/mod-host.h | 1 + 3 files changed, 12 insertions(+) diff --git a/README.md b/README.md index d3526b71..3c6fa02e 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,9 @@ The commands supported by mod-host are: * all instances must be in deactivated state e.g.: multi_pre_run 1 2 0 1 2 "gain" 0.0 "distortion" 0.5 + wait_audio_cycle + * wait for at least 1 audio cycle to pass + help * show a help message diff --git a/src/mod-host.c b/src/mod-host.c index 36d964a8..5dc3c820 100644 --- a/src/mod-host.c +++ b/src/mod-host.c @@ -74,6 +74,7 @@ #include "protocol.h" #include "completer.h" #include "monitor.h" +#include "monitor/monitor-client.h" #include "zix/thread.h" #include "info.h" @@ -1011,6 +1012,12 @@ static void multi_pre_run(proto_t *proto) free(params); } +static void wait_audio_cycle(proto_t *proto) +{ + int resp = monitor_client_wait_proc() ? 0 : ERR_INVALID_OPERATION; + protocol_response_int(resp, proto); +} + static void help_cb(proto_t *proto) { proto->response = 0; @@ -1186,6 +1193,7 @@ static int mod_host_init(jack_client_t* client, int socket_port, int feedback_po protocol_add_command(MULTI_PARAM_SET, multi_param_set); protocol_add_command(MULTI_PARAMS_FLUSH, multi_params_flush); protocol_add_command(MULTI_PRE_RUN, multi_pre_run); + protocol_add_command(WAIT_AUDIO_CYCLE, wait_audio_cycle); /* skip help and quit for internal client */ if (client == NULL) diff --git a/src/mod-host.h b/src/mod-host.h index 7e9771f8..c6533f38 100644 --- a/src/mod-host.h +++ b/src/mod-host.h @@ -113,6 +113,7 @@ #define MULTI_PARAM_SET "multi_param_set %s %f %i ..." #define MULTI_PARAMS_FLUSH "multi_params_flush %i %i ... %i ..." #define MULTI_PRE_RUN "multi_pre_run %i %i ... %i ..." +#define WAIT_AUDIO_CYCLE "wait_audio_cycle" #define HELP "help" #define QUIT "quit" From 28c9f2b180732624c37ad1607e5e49bd529cc679 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Apr 2026 12:07:07 +0200 Subject: [PATCH 12/12] Update new command documentation Signed-off-by: falkTX --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c6fa02e..50e064a7 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ The commands supported by mod-host are: pre_run * pre-run and flush several param values at once and trigger reset if available * reset value must be according to reset property spec - * instance must be in deactivated state + * instance must be in deactivated state or global processing disabled (and plugin does not have isLive flag) e.g.: pre_run 0 1 2 "gain" 0.0 "distortion" 0.5 patch_set @@ -353,7 +353,7 @@ The commands supported by mod-host are: multi_pre_run * pre-run and flush several param values at once and trigger reset if available (multiple instance variant) * reset value must be according to reset property spec - * all instances must be in deactivated state + * all instances must be in deactivated state or global processing disabled (and plugins do not have isLive flag) e.g.: multi_pre_run 1 2 0 1 2 "gain" 0.0 "distortion" 0.5 wait_audio_cycle