@@ -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+
614619typedef struct POSTPONED_PARAMETER_EVENT_T {
615620 int effect_id ;
616621 const char * symbol ;
@@ -725,6 +730,7 @@ typedef struct RAW_MIDI_PORT_ITEM {
725730typedef 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;
861867static bool g_monitored_midi_controls [16 ];
862868static 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
865876static hylia_t * g_hylia_instance ;
866877static hylia_time_info_t g_hylia_timeinfo ;
@@ -911,6 +922,7 @@ static void* PostPonedEventsThread(void* arg);
911922#ifdef MOD_HMI_CONTROL_ENABLED
912923static void * HMIClientThread (void * arg );
913924#endif
925+ static void SyncScheduledParams (bool realtime );
914926static void PreRunPlugin (effect_t * effect );
915927static int ProcessPlugin (jack_nframes_t nframes , void * arg );
916928static 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+
19041943static 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
0 commit comments