diff --git a/README.md b/README.md index f124cd5..50e064a 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 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 * set the value of a control port e.g.: patch_set 0 "gain" 2.5 @@ -341,8 +347,18 @@ 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 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 + * wait for at least 1 audio cycle to pass + help * show a help message diff --git a/src/effects.c b/src/effects.c index 236b558..cd18fb7 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 @@ -926,6 +926,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); @@ -1916,6 +1917,122 @@ static void* HMIClientThread(void* arg) } #endif +static void PreRunPlugin(effect_t *effect) +{ + if (effect->hints & HINT_STATE_UNSAFE) + pthread_mutex_lock(&effect->state_restore_mutex); + + /* 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; @@ -5143,7 +5260,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); @@ -5152,21 +5269,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; @@ -5181,7 +5298,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); } @@ -5192,7 +5309,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 @@ -6551,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); } } @@ -6644,9 +6761,13 @@ 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) + effect->activated = true; + + lilv_instance_activate(effect->lilv_instance); + + if (jack_activate(effect->jack_client) != 0) fprintf(stderr, "can't activate jack_client\n"); return NULL; @@ -6654,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; } @@ -6679,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]; @@ -6692,23 +6817,20 @@ 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->jack_client) == 0) + if (zix_thread_create(&threads[num_threads], sizeof(void*), effects_activate_thread, effect) == 0) ++num_threads; else - jack_activate(effect->jack_client); + effects_activate_thread(effect); } } else { 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); } } } @@ -6719,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; } @@ -7012,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); @@ -7087,6 +7199,93 @@ 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 && ((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; + } + + 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; + + 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 && ((effect->hints & HINT_IS_LIVE) != 0 || g_processing_enabled)) + { + fprintf(stderr, "multi-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) { @@ -9150,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 @@ -9166,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/effects.h b/src/effects.h index 80b08d5..d266376 100644 --- a/src/effects.h +++ b/src/effects.h @@ -160,6 +160,8 @@ 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); 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 f9c9b99..5dc3c82 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" @@ -312,6 +313,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; @@ -924,6 +958,66 @@ 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 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; @@ -1053,6 +1147,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); @@ -1097,6 +1192,8 @@ 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); + 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 6778f7c..c6533f3 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" @@ -111,6 +112,8 @@ #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 WAIT_AUDIO_CYCLE "wait_audio_cycle" #define HELP "help" #define QUIT "quit" diff --git a/src/monitor/monitor-client.c b/src/monitor/monitor-client.c index b8153cd..0daf5a9 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 f6fe657..c6d706d 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);