Skip to content

Commit 2233be1

Browse files
committed
Allow to schedule multi param changes so they happen in sync
Signed-off-by: falkTX <falktx@falktx.com>
1 parent 324f386 commit 2233be1

File tree

2 files changed

+126
-36
lines changed

2 files changed

+126
-36
lines changed

src/effects.c

Lines changed: 124 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,11 @@ typedef struct HMI_ADDRESSING_T {
611611
} hmi_addressing_t;
612612
#endif
613613

614+
typedef struct SYNC_SCHEDULED_PARAM_T {
615+
port_t *port;
616+
float value;
617+
} sync_scheduled_param_t;
618+
614619
typedef struct POSTPONED_PARAMETER_EVENT_T {
615620
int effect_id;
616621
const char* symbol;
@@ -725,6 +730,7 @@ typedef struct RAW_MIDI_PORT_ITEM {
725730
typedef struct CACHED_EFFECT_FLUSH_T {
726731
effect_t *effect;
727732
port_t **ports;
733+
int num_ports;
728734
} cached_effect_flush_t;
729735

730736

@@ -861,6 +867,11 @@ static pthread_mutex_t g_midi_learning_mutex;
861867
static bool g_monitored_midi_controls[16];
862868
static bool g_monitored_midi_programs[16];
863869

870+
/* Postponed port updates */
871+
static sync_scheduled_param_t g_sync_scheduled_params[MAX_SYNC_SCHEDULED_PARAMS];
872+
static pthread_mutex_t g_sync_scheduled_params_mutex;
873+
static unsigned int g_sync_scheduled_param_count;
874+
864875
#ifdef HAVE_HYLIA
865876
static hylia_t* g_hylia_instance;
866877
static hylia_time_info_t g_hylia_timeinfo;
@@ -911,6 +922,7 @@ static void* PostPonedEventsThread(void* arg);
911922
#ifdef MOD_HMI_CONTROL_ENABLED
912923
static void* HMIClientThread(void* arg);
913924
#endif
925+
static void SyncScheduledParams(bool realtime);
914926
static void PreRunPlugin(effect_t *effect);
915927
static int ProcessPlugin(jack_nframes_t nframes, void *arg);
916928
static bool SetPortValue(port_t *port, float value, int effect_id, bool is_bypass, bool from_ui);
@@ -1901,6 +1913,33 @@ static void* HMIClientThread(void* arg)
19011913
}
19021914
#endif
19031915

1916+
static void SyncScheduledParams(bool realtime)
1917+
{
1918+
port_t *port;
1919+
1920+
if (realtime)
1921+
{
1922+
if (pthread_mutex_trylock(&g_sync_scheduled_params_mutex) != 0)
1923+
return;
1924+
}
1925+
else
1926+
{
1927+
pthread_mutex_lock(&g_sync_scheduled_params_mutex);
1928+
}
1929+
1930+
for (unsigned int i = 0; i < g_sync_scheduled_param_count; i++)
1931+
{
1932+
port = g_sync_scheduled_params[i].port;
1933+
port->prev_value = *port->buffer = g_sync_scheduled_params[i].value;
1934+
#ifdef WITH_EXTERNAL_UI_SUPPORT
1935+
port->hints |= HINT_SHOULD_UPDATE;
1936+
#endif
1937+
}
1938+
1939+
g_sync_scheduled_param_count = 0;
1940+
pthread_mutex_unlock(&g_sync_scheduled_params_mutex);
1941+
}
1942+
19041943
static void PreRunPlugin(effect_t *effect)
19051944
{
19061945
if ((effect->hints & HINT_IS_LIVE) == 0 &&
@@ -3369,6 +3408,9 @@ static void JackTimebase(jack_transport_state_t state, jack_nframes_t nframes,
33693408

33703409
pos->tick = (int32_t)(tick + 0.5);
33713410
pos->tick_double = tick;
3411+
3412+
// (ab)use this early jack callback to change parameters in sync
3413+
SyncScheduledParams(true);
33723414
return;
33733415

33743416
UNUSED_PARAM(state);
@@ -4498,6 +4540,7 @@ int effects_init(void* client)
44984540
pthread_mutex_init(&g_raw_midi_port_mutex, &mutex_atts);
44994541
pthread_mutex_init(&g_audio_monitor_mutex, &mutex_atts);
45004542
pthread_mutex_init(&g_midi_learning_mutex, &mutex_atts);
4543+
pthread_mutex_init(&g_sync_scheduled_params_mutex, &mutex_atts);
45014544
#ifdef MOD_HMI_CONTROL_ENABLED
45024545
pthread_mutex_init(&g_hmi_mutex, &mutex_atts);
45034546
#endif
@@ -4644,7 +4687,7 @@ int effects_init(void* client)
46444687

46454688
/* Set jack callbacks */
46464689
jack_set_thread_init_callback(g_jack_global_client, JackThreadInit, NULL);
4647-
jack_set_timebase_callback(g_jack_global_client, 1, JackTimebase, NULL);
4690+
jack_set_timebase_callback(g_jack_global_client, 0, JackTimebase, NULL);
46484691
jack_set_process_callback(g_jack_global_client, ProcessGlobalClient, NULL);
46494692
jack_set_buffer_size_callback(g_jack_global_client, BufferSize, NULL);
46504693
jack_set_port_registration_callback(g_jack_global_client, PortRegistration, NULL);
@@ -5084,6 +5127,7 @@ int effects_finish(int close_client)
50845127
pthread_mutex_destroy(&g_raw_midi_port_mutex);
50855128
pthread_mutex_destroy(&g_audio_monitor_mutex);
50865129
pthread_mutex_destroy(&g_midi_learning_mutex);
5130+
pthread_mutex_destroy(&g_sync_scheduled_params_mutex);
50875131
#ifdef MOD_HMI_CONTROL_ENABLED
50885132
pthread_mutex_destroy(&g_hmi_mutex);
50895133
#endif
@@ -6561,11 +6605,19 @@ int effects_remove(int effect_id)
65616605

65626606
start = 0;
65636607
end = MAX_PLUGIN_INSTANCES;
6608+
6609+
// clear all sync scheduled params if removing all plugins
6610+
pthread_mutex_lock(&g_sync_scheduled_params_mutex);
6611+
g_sync_scheduled_param_count = 0;
6612+
pthread_mutex_unlock(&g_sync_scheduled_params_mutex);
65646613
}
65656614
else
65666615
{
65676616
start = effect_id;
65686617
end = start + 1;
6618+
6619+
// trigger sync scheduled params now for no dangling pointers
6620+
SyncScheduledParams(false);
65696621
}
65706622

65716623
// stop plugins processing
@@ -7000,17 +7052,36 @@ int effects_set_parameter_multi(const char *control_symbol, float value, int num
70007052
}
70017053

70027054
// the critical loop, must be as fast and small as possible
7003-
port_t *port;
7004-
for (int i = 0; i < num_ports; i++)
7055+
bool scheduled = true;
7056+
pthread_mutex_lock(&g_sync_scheduled_params_mutex);
7057+
if (g_sync_scheduled_param_count + num_ports <= MAX_SYNC_SCHEDULED_PARAMS)
7058+
{
7059+
int p = g_sync_scheduled_param_count;
7060+
for (int i = 0; i < num_ports; i++, p++)
7061+
{
7062+
g_sync_scheduled_params[p].port = ports[i];
7063+
g_sync_scheduled_params[p].value = value;
7064+
}
7065+
g_sync_scheduled_param_count = p;
7066+
}
7067+
else
70057068
{
7006-
port = ports[i];
7007-
port->prev_value = *port->buffer = value;
7069+
scheduled = false;
70087070
}
7071+
pthread_mutex_unlock(&g_sync_scheduled_params_mutex);
70097072

7073+
if (! scheduled)
7074+
{
7075+
// no space to schedule events, trigger param changes now
7076+
for (int i = 0; i < num_ports; i++)
7077+
{
7078+
port_t *port = ports[i];
7079+
port->prev_value = *port->buffer = value;
70107080
#ifdef WITH_EXTERNAL_UI_SUPPORT
7011-
for (int i = 0; i < num_ports; i++)
7012-
ports[i]->hints |= HINT_SHOULD_UPDATE;
7081+
port->hints |= HINT_SHOULD_UPDATE;
70137082
#endif
7083+
}
7084+
}
70147085

70157086
free(ports);
70167087

@@ -7080,6 +7151,7 @@ int effects_flush_parameters_multi(int reset, int param_count, const flushed_par
70807151
if (num_effects == 1)
70817152
return effects_flush_parameters(*effects, reset, param_count, params);
70827153

7154+
cached_effect_flush_t *cached_effect;
70837155
effect_t *effect;
70847156
port_t *port;
70857157

@@ -7096,54 +7168,70 @@ int effects_flush_parameters_multi(int reset, int param_count, const flushed_par
70967168
{
70977169
effect = &(g_effects[effect_id]);
70987170

7099-
cached_effect_flush_t *cached_effect = &cached_effects[num_cached_effects++];
7171+
cached_effect = &cached_effects[num_cached_effects++];
71007172
cached_effect->effect = effect;
71017173
cached_effect->ports = malloc(sizeof(port_t*) * param_count);
7174+
cached_effect->num_ports = 0;
71027175

71037176
for (int j = 0; j < param_count; j++)
7104-
cached_effect->ports[j] = FindEffectInputPortBySymbol(effect, params[j].symbol);
7177+
{
7178+
if ((cached_effect->ports[cached_effect->num_ports] = FindEffectInputPortBySymbol(effect, params[j].symbol)))
7179+
++cached_effect->num_ports;
7180+
}
71057181
}
71067182
}
71077183

7108-
for (int i = 0; i < num_cached_effects; i++)
7184+
// the critical loop, must be as fast and small as possible
7185+
bool scheduled = true;
7186+
const int resetb = reset != 0 ? 1 : 0;
7187+
pthread_mutex_lock(&g_sync_scheduled_params_mutex);
7188+
if (g_sync_scheduled_param_count + (param_count + resetb) * num_cached_effects <= MAX_SYNC_SCHEDULED_PARAMS)
71097189
{
7110-
effect = cached_effects[i].effect;
7111-
if (effect->reset_index >= 0 && reset != 0)
7190+
int p = g_sync_scheduled_param_count;
7191+
for (int i = 0; i < num_cached_effects; i++)
71127192
{
7113-
port = effect->ports[effect->reset_index];
7114-
port->prev_value = *(port->buffer) = reset;
7193+
cached_effect = &cached_effects[i];
7194+
effect = cached_effect->effect;
7195+
for (int j = 0; j < cached_effect->num_ports; j++, p++)
7196+
{
7197+
g_sync_scheduled_params[p].port = cached_effect->ports[j];
7198+
g_sync_scheduled_params[p].value = params[j].value;
7199+
}
7200+
if (effect->reset_index >= 0 && reset != 0)
7201+
{
7202+
g_sync_scheduled_params[p].port = effect->ports[effect->reset_index];
7203+
g_sync_scheduled_params[p].value = reset;
7204+
++p;
7205+
}
71157206
}
7207+
g_sync_scheduled_param_count = p;
7208+
}
7209+
else
7210+
{
7211+
scheduled = false;
71167212
}
7213+
pthread_mutex_unlock(&g_sync_scheduled_params_mutex);
71177214

7118-
// the critical loop, must be as fast and small as possible
7119-
for (int j = 0; j < param_count; j++)
7215+
if (! scheduled)
71207216
{
7217+
// no space to schedule events, trigger param changes now
71217218
for (int i = 0; i < num_cached_effects; i++)
71227219
{
7123-
if ((port = cached_effects[i].ports[j]))
7220+
cached_effect = &cached_effects[i];
7221+
effect = cached_effect->effect;
7222+
for (int j = 0; j < cached_effect->num_ports; j++)
7223+
{
7224+
port = cached_effect->ports[j];
71247225
port->prev_value = *(port->buffer) = params[j].value;
7125-
}
7126-
}
7127-
71287226
#ifdef WITH_EXTERNAL_UI_SUPPORT
7129-
for (int i = 0; i < num_cached_effects; i++)
7130-
{
7131-
for (int j = 0; j < param_count; j++)
7132-
{
7133-
if ((port = cached_effects[i].ports[j]))
71347227
port->hints |= HINT_SHOULD_UPDATE;
7135-
}
7136-
}
71377228
#endif
7138-
7139-
// reset a 2nd time in case plugin was processing while we changed parameters
7140-
for (int i = 0; i < num_cached_effects; i++)
7141-
{
7142-
effect = cached_effects[i].effect;
7143-
if (effect->reset_index >= 0 && reset != 0)
7144-
{
7145-
port = effect->ports[effect->reset_index];
7146-
port->prev_value = *(port->buffer) = reset;
7229+
}
7230+
if (effect->reset_index >= 0 && reset != 0)
7231+
{
7232+
port = effect->ports[effect->reset_index];
7233+
port->prev_value = *(port->buffer) = reset;
7234+
}
71477235
}
71487236
}
71497237

src/effects.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ typedef enum {
9797
#define MAX_POSTPONED_EVENTS 8192
9898
#define MAX_HMI_ADDRESSINGS 128
9999

100+
#define MAX_SYNC_SCHEDULED_PARAMS 512
101+
100102
// used for local stack variables
101103
#define MAX_CHAR_BUF_SIZE 255
102104

0 commit comments

Comments
 (0)