1 // effects-weed.c
2 // LiVES (lives-exe)
3 // (c) G. Finch 2005 - 2020 (salsaman+lives@gmail.com)
4 // Released under the GPL 3 or later
5 // see file ../COPYING for licensing details
6 
7 #include <dlfcn.h>
8 
9 #ifdef __cplusplus
10 #if defined(HAVE_OPENCV) || defined(HAVE_OPENCV4)
11 #ifdef HAVE_OPENCV4
12 #include "opencv4/opencv2/core/core.hpp"
13 #else
14 #include "opencv2/core/core.hpp"
15 #endif
16 using namespace cv;
17 #endif
18 #endif
19 
20 #include "main.h"
21 #include "effects.h"
22 
23 #include "weed-effects-utils.h"
24 
25 ///////////////////////////////////
26 
27 #include "callbacks.h"
28 #include "rte_window.h"
29 #include "resample.h"
30 #include "audio.h"
31 #include "ce_thumbs.h"
32 #include "paramwindow.h"
33 
34 ////////////////////////////////////////////////////////////////////////
35 
36 static int our_plugin_id = 0;
37 static weed_plant_t *expected_hi = NULL, *expected_pi = NULL;
38 static boolean suspect = FALSE;
39 static char *fxname;
40 static int ncbcalls = 0;
41 
42 static boolean fx_inited = FALSE;
43 
44 struct _procvals {
45   weed_process_f procfunc;
46   weed_plant_t *inst;
47   weed_timecode_t tc;
48   weed_error_t ret;
49   char padding[DEF_ALIGN - ((sizeof(weed_process_f) - sizeof(weed_plant_t *)
50                              - sizeof(weed_timecode_t) - sizeof(weed_error_t)) % DEF_ALIGN)];
51 };
52 
53 static weed_plantptr_t statsplant = NULL;
54 
55 static int load_compound_fx(void);
56 
57 
58 #if 0
59 LIVES_LOCAL_INLINE int weed_inst_refs_count(weed_plant_t *inst) {
60   int error;
61   if (!inst) return -1;
62   if (!weed_plant_has_leaf(inst, WEED_LEAF_HOST_REFS)) return 0;
63   return weed_get_int_value(inst, WEED_LEAF_HOST_REFS, &error);
64 }
65 #endif
66 ////////////////////////////////////////////////////////////////////////////
67 
weed_leaf_copy_or_delete(weed_layer_t * dlayer,const char * key,weed_layer_t * slayer)68 LIVES_GLOBAL_INLINE weed_error_t weed_leaf_copy_or_delete(weed_layer_t *dlayer, const char *key, weed_layer_t *slayer) {
69   if (!weed_plant_has_leaf(slayer, key)) return weed_leaf_delete(dlayer, key);
70   else return weed_leaf_copy(dlayer, key, slayer, key);
71 }
72 
73 
filter_mutex_trylock(int key)74 LIVES_GLOBAL_INLINE int filter_mutex_trylock(int key) {
75 #ifdef DEBUG_FILTER_MUTEXES
76   int retval;
77   g_print("trylock of %d\n", key);
78 #endif
79   if (key < 0 || key >= FX_KEYS_MAX) {
80     if (key != -1) {
81       char *msg = lives_strdup_printf("attempted lock of bad fx key %d", key);
82       LIVES_ERROR(msg);
83       lives_free(msg);
84     }
85     return 0;
86   }
87 #ifdef DEBUG_FILTER_MUTEXES
88   retval = pthread_mutex_trylock(&mainw->fx_mutex[key]);
89   g_print("trylock of %d returned %d\n", key, retval);
90   return retval;
91 #endif
92   return pthread_mutex_trylock(&mainw->fx_mutex[key]);
93 }
94 
95 #ifndef DEBUG_FILTER_MUTEXES
filter_mutex_lock(int key)96 LIVES_GLOBAL_INLINE int filter_mutex_lock(int key) {
97   int ret = filter_mutex_trylock(key);
98   /// attempted double locking is actually normal behaviour when we have both video and audio effects running
99   /* if (ret != 0 && !mainw->is_exiting) { */
100   /*   /\* char *msg = lives_strdup_printf("attempted double lock of fx key %d, err was %d", key, ret); *\/ */
101   /*   /\* LIVES_ERROR(msg); *\/ */
102   /*   /\* lives_free(msg); *\/ */
103   /* } */
104   return ret;
105 }
106 
107 
filter_mutex_unlock(int key)108 LIVES_GLOBAL_INLINE int filter_mutex_unlock(int key) {
109   if (key >= 0 && key < FX_KEYS_MAX) {
110     int ret = pthread_mutex_unlock(&mainw->fx_mutex[key]);
111     if (ret != 0 && !mainw->is_exiting) {
112       char *msg = lives_strdup_printf("attempted double unlock of fx key %d, err was %d", key, ret);
113       LIVES_ERROR(msg);
114       lives_free(msg);
115     }
116     return ret;
117   } else {
118     if (key != -1) {
119       char *msg = lives_strdup_printf("attempted unlock of bad fx key %d", key);
120       LIVES_ERROR(msg);
121       lives_free(msg);
122     }
123   }
124   return 0;
125 }
126 #endif
127 
128 
129 //////////////////////////////////////////////////////////////////////////////
130 // filter library functions
131 
num_compound_fx(weed_plant_t * plant)132 int num_compound_fx(weed_plant_t *plant) {
133   weed_plant_t *filter;
134 
135   if (WEED_PLANT_IS_FILTER_INSTANCE(plant)) filter = weed_instance_get_filter(plant, TRUE);
136   else filter = plant;
137 
138   if (!weed_plant_has_leaf(filter, WEED_LEAF_HOST_FILTER_LIST)) return 1;
139   return weed_leaf_num_elements(filter, WEED_LEAF_HOST_FILTER_LIST);
140 }
141 
142 
has_non_alpha_palette(weed_plant_t * ctmpl,weed_plant_t * filter)143 boolean has_non_alpha_palette(weed_plant_t *ctmpl, weed_plant_t *filter) {
144   int *plist;
145   int npals = 0;
146   register int i;
147 
148   plist = weed_chantmpl_get_palette_list(filter, ctmpl, &npals);
149   if (!plist) return TRUE; ///< probably audio
150   for (i = 0; i < npals; i++) {
151     if (plist[i] == WEED_PALETTE_END) break;
152     if (!weed_palette_is_alpha(plist[i])) {
153       lives_free(plist);
154       return TRUE;
155     }
156   }
157   lives_free(plist);
158   return FALSE;
159 }
160 
161 
has_alpha_palette(weed_plant_t * ctmpl,weed_plant_t * filter)162 boolean has_alpha_palette(weed_plant_t *ctmpl, weed_plant_t *filter) {
163   int *plist;
164   int npals = 0;
165   register int i;
166 
167   plist = weed_chantmpl_get_palette_list(filter, ctmpl, &npals);
168   for (i = 0; i < npals; i++) {
169     if (weed_palette_is_alpha(plist[i])) {
170       if (palette == WEED_PALETTE_NONE) continue;
171       lives_free(plist);
172       return TRUE;
173     }
174   }
175   lives_free(plist);
176   return FALSE;
177 }
178 
179 
weed_instance_get_filter(weed_plant_t * inst,boolean get_compound_parent)180 weed_plant_t *weed_instance_get_filter(weed_plant_t *inst, boolean get_compound_parent) {
181   if (get_compound_parent &&
182       (weed_plant_has_leaf(inst, WEED_LEAF_HOST_COMPOUND_CLASS)))
183     return weed_get_plantptr_value(inst, WEED_LEAF_HOST_COMPOUND_CLASS, NULL);
184   return weed_get_plantptr_value(inst, WEED_LEAF_FILTER_CLASS, NULL);
185 }
186 
187 
all_outs_alpha(weed_plant_t * filt,boolean ign_opt)188 static boolean all_outs_alpha(weed_plant_t *filt, boolean ign_opt) {
189   // check (mandatory) output chans, see if any are non-alpha
190   int nouts;
191   weed_plant_t **ctmpls = weed_filter_get_out_chantmpls(filt, &nouts);
192   if (!nouts) return FALSE;
193   if (!ctmpls[0]) {
194     lives_freep((void **)&ctmpls);
195     return FALSE;
196   }
197   for (int i = 0; i < nouts; i++) {
198     if (ign_opt && weed_chantmpl_is_optional(ctmpls[i])) continue;
199     if (has_non_alpha_palette(ctmpls[i], filt)) {
200       lives_free(ctmpls);
201       return FALSE;
202     }
203   }
204   lives_freep((void **)&ctmpls);
205   return TRUE;
206 }
207 
208 
all_ins_alpha(weed_plant_t * filt,boolean ign_opt)209 static boolean all_ins_alpha(weed_plant_t *filt, boolean ign_opt) {
210   // check mandatory input chans, see if any are non-alpha
211   // if there are no mandatory inputs, we check optional (even if ign_opt is TRUE)
212   boolean has_mandatory_in = FALSE;
213   int nins;
214   weed_plant_t **ctmpls = weed_filter_get_in_chantmpls(filt, &nins);
215   if (nins == 0) return FALSE;
216   if (!ctmpls[0]) {
217     lives_free(ctmpls);
218     return FALSE;
219   }
220   for (int i = 0; i < nins; i++) {
221     if (ign_opt && weed_chantmpl_is_optional(ctmpls[i])) continue;
222     has_mandatory_in = TRUE;
223     if (has_non_alpha_palette(ctmpls[i], filt)) {
224       lives_free(ctmpls);
225       return FALSE;
226     }
227   }
228   if (!has_mandatory_in) {
229     for (int i = 0; i < nins; i++) {
230       if (has_non_alpha_palette(ctmpls[i], filt)) {
231         lives_free(ctmpls);
232         return FALSE;
233       }
234     }
235     lives_free(ctmpls);
236     return TRUE;
237   }
238   lives_free(ctmpls);
239   return FALSE;
240 }
241 
242 
weed_filter_categorise(weed_plant_t * pl,int in_channels,int out_channels)243 lives_fx_cat_t weed_filter_categorise(weed_plant_t *pl, int in_channels, int out_channels) {
244   weed_plant_t *filt = pl;
245 
246   boolean has_out_params = FALSE;
247   boolean has_in_params = FALSE;
248   boolean all_out_alpha = TRUE;
249   boolean all_in_alpha = TRUE;
250   boolean has_in_alpha = FALSE;
251 
252   int filter_flags;
253 
254   if (WEED_PLANT_IS_FILTER_INSTANCE(pl)) filt = weed_instance_get_filter(pl, TRUE);
255 
256   all_out_alpha = all_outs_alpha(filt, TRUE);
257   all_in_alpha = all_ins_alpha(filt, TRUE);
258 
259   filter_flags = weed_get_int_value(filt, WEED_LEAF_FLAGS, NULL);
260   if (weed_plant_has_leaf(filt, WEED_LEAF_OUT_PARAMETER_TEMPLATES)) has_out_params = TRUE;
261   if (weed_plant_has_leaf(filt, WEED_LEAF_IN_PARAMETER_TEMPLATES)) has_in_params = TRUE;
262   if (filter_flags & WEED_FILTER_IS_CONVERTER) return LIVES_FX_CAT_CONVERTER;
263   if (in_channels == 0 && out_channels > 0 && all_out_alpha) return LIVES_FX_CAT_DATA_GENERATOR;
264   if (in_channels == 0 && out_channels > 0) {
265     if (!has_audio_chans_out(filt, TRUE)) return LIVES_FX_CAT_VIDEO_GENERATOR;
266     else if (has_video_chans_out(filt, TRUE)) return LIVES_FX_CAT_AV_GENERATOR;
267     else return LIVES_FX_CAT_AUDIO_GENERATOR;
268   }
269   if (out_channels >= 1 && in_channels >= 1 && (all_in_alpha || has_in_alpha) && !all_out_alpha)
270     return LIVES_FX_CAT_DATA_VISUALISER;
271   if (out_channels >= 1 && all_out_alpha) return LIVES_FX_CAT_ANALYSER;
272   if (out_channels > 1) return LIVES_FX_CAT_SPLITTER;
273 
274   if (in_channels > 2 && out_channels == 1) {
275     return LIVES_FX_CAT_COMPOSITOR;
276   }
277   if (in_channels == 2 && out_channels == 1) return LIVES_FX_CAT_TRANSITION;
278   if (in_channels == 1 && out_channels == 1 && !(has_video_chans_in(filt, TRUE)) &&
279       !(has_video_chans_out(filt, TRUE))) return LIVES_FX_CAT_AUDIO_EFFECT;
280   if (in_channels == 1 && out_channels == 1) return LIVES_FX_CAT_EFFECT;
281   if (in_channels > 0 && out_channels == 0 && has_out_params) return LIVES_FX_CAT_ANALYSER;
282   if (in_channels > 0 && out_channels == 0) return LIVES_FX_CAT_TAP;
283   if (in_channels == 0 && out_channels == 0 && has_out_params && has_in_params) return LIVES_FX_CAT_DATA_PROCESSOR;
284   if (in_channels == 0 && out_channels == 0 && has_out_params) return LIVES_FX_CAT_DATA_SOURCE;
285   if (in_channels == 0 && out_channels == 0) return LIVES_FX_CAT_UTILITY;
286   return LIVES_FX_CAT_NONE;
287 }
288 
289 
weed_filter_subcategorise(weed_plant_t * pl,lives_fx_cat_t category,boolean count_opt)290 lives_fx_cat_t weed_filter_subcategorise(weed_plant_t *pl, lives_fx_cat_t category, boolean count_opt) {
291   weed_plant_t *filt = pl;
292   boolean has_video_chansi;
293 
294   if (WEED_PLANT_IS_FILTER_INSTANCE(pl)) filt = weed_instance_get_filter(pl, TRUE);
295 
296   if (category == LIVES_FX_CAT_COMPOSITOR) count_opt = TRUE;
297 
298   has_video_chansi = has_video_chans_in(filt, count_opt);
299 
300   if (category == LIVES_FX_CAT_TRANSITION) {
301     if (get_transition_param(filt, FALSE) != -1) {
302       if (!has_video_chansi) return LIVES_FX_CAT_AUDIO_TRANSITION;
303       return LIVES_FX_CAT_AV_TRANSITION;
304     }
305     return LIVES_FX_CAT_VIDEO_TRANSITION;
306   }
307 
308   if (category == LIVES_FX_CAT_COMPOSITOR && !has_video_chansi) return LIVES_FX_CAT_AUDIO_MIXER;
309   if (category == LIVES_FX_CAT_EFFECT && !has_video_chansi) return LIVES_FX_CAT_AUDIO_EFFECT;
310   if (category == LIVES_FX_CAT_CONVERTER && !has_video_chansi) return LIVES_FX_CAT_AUDIO_VOL;
311 
312   if (category == LIVES_FX_CAT_ANALYSER) {
313     if (!has_video_chansi) return LIVES_FX_CAT_AUDIO_ANALYSER;
314     return LIVES_FX_CAT_VIDEO_ANALYSER;
315   }
316 
317   return LIVES_FX_CAT_NONE;
318 }
319 
320 
num_alpha_channels(weed_plant_t * filter,boolean out)321 int num_alpha_channels(weed_plant_t *filter, boolean out) {
322   // get number of alpha channels (in or out) for filter; including optional
323   weed_plant_t **ctmpls;
324   int count = 0, nchans;
325   register int i;
326 
327   if (out) {
328     ctmpls = weed_filter_get_out_chantmpls(filter, &nchans);
329     if (!ctmpls) return FALSE;
330     for (i = 0; i < nchans; i++) {
331       if (has_non_alpha_palette(ctmpls[i], filter)) continue;
332       count++;
333     }
334   } else {
335     ctmpls = weed_filter_get_in_chantmpls(filter, &nchans);
336     if (!ctmpls) return FALSE;
337     for (i = 0; i < nchans; i++) {
338       if (has_non_alpha_palette(ctmpls[i], filter)) continue;
339       count++;
340     }
341   }
342   lives_freep((void **)&ctmpls);
343   return count;
344 }
345 
346 
347 ////////////////////////////////////////////////////////////////////////
348 
349 #define MAX_WEED_INSTANCES 65536
350 
351 // store keys so we now eg, which rte mask entry to xor when deiniting
352 static int fg_generator_key;
353 static int bg_generator_key;
354 
355 // store modes too
356 static int fg_generator_mode;
357 static int bg_generator_mode;
358 
359 // generators to start on playback, because of a problem in libvisual
360 // - we must start generators after starting audio
361 static int bg_gen_to_start;
362 static int fg_gen_to_start;
363 
364 // store the clip, this can sometimes get lost
365 static int fg_generator_clip;
366 
367 //////////////////////////////////////////////////////////////////////
368 
369 static weed_plant_t **weed_filters; // array of filter_classes
370 
371 static LiVESList *weed_fx_sorted_list;
372 
373 // each 'hotkey' controls n instances, selectable as 'modes' or banks
374 static weed_plant_t **key_to_instance[FX_KEYS_MAX];
375 static weed_plant_t **key_to_instance_copy[FX_KEYS_MAX]; // copy for preview during rendering
376 
377 static int *key_to_fx[FX_KEYS_MAX];
378 static int key_modes[FX_KEYS_MAX];
379 
380 static int num_weed_filters; ///< count of how many filters we have loaded
381 
382 
383 typedef struct {
384   char *string;
385   int32_t hash;
386 } lives_hashentry;
387 
388 #define NHASH_TYPES 8
389 
390 typedef lives_hashentry  lives_hashjoint[NHASH_TYPES];
391 
392 // 0 is TF, 1 is TT, 2 is FF, 3 is FT // then possibly repeated with spaces substituted with '_'
393 static lives_hashjoint *hashnames;
394 
395 // per key/mode parameter defaults
396 // *INDENT-OFF*
397 weed_plant_t ***key_defaults[FX_KEYS_MAX_VIRTUAL];
398 // *INDENT-ON*
399 
400 /////////////////// LiVES event system /////////////////
401 
402 static weed_plant_t *init_events[FX_KEYS_MAX_VIRTUAL];
403 static void **pchains[FX_KEYS_MAX]; // parameter changes, used during recording (not for rendering)
404 static void *filter_map[FX_KEYS_MAX + 2];
405 static int next_free_key;
406 
407 ////////////////////////////////////////////////////////////////////
408 
409 
backup_weed_instances(void)410 void backup_weed_instances(void) {
411   // this is called during multitrack rendering.
412   // We are rendering, but we want to display the current frame in the preview window
413   // thus we backup our rendering instances, apply the current frame instances, and then restore the rendering instances
414   register int i;
415 
416   for (i = FX_KEYS_MAX_VIRTUAL; i < FX_KEYS_MAX; i++) {
417     key_to_instance_copy[i][0] = key_to_instance[i][0];
418     key_to_instance[i][0] = NULL;
419   }
420 }
421 
422 
restore_weed_instances(void)423 void restore_weed_instances(void) {
424   register int i;
425   for (i = FX_KEYS_MAX_VIRTUAL; i < FX_KEYS_MAX; i++) {
426     key_to_instance[i][0] = key_to_instance_copy[i][0];
427   }
428 }
429 
430 
weed_filter_get_type(weed_plant_t * filter,boolean getsub,boolean format)431 static char *weed_filter_get_type(weed_plant_t *filter, boolean getsub, boolean format) {
432   // return value should be lives_free'd after use
433   char *tmp1, *tmp2, *ret;
434   lives_fx_cat_t cat = weed_filter_categorise(filter,
435                        enabled_in_channels(filter, TRUE),
436                        enabled_out_channels(filter, TRUE));
437 
438   lives_fx_cat_t sub = weed_filter_subcategorise(filter, cat, FALSE);
439 
440   if (!getsub || sub == LIVES_FX_CAT_NONE)
441     return lives_fx_cat_to_text(cat, FALSE);
442 
443   tmp1 = lives_fx_cat_to_text(cat, FALSE);
444   tmp2 = lives_fx_cat_to_text(sub, FALSE);
445 
446   if (format) ret = lives_strdup_printf("%s (%s)", tmp1, tmp2);
447   else {
448     if (cat == LIVES_FX_CAT_TRANSITION) ret = lives_strdup_printf("%s - %s", tmp1, tmp2);
449     else ret = lives_strdup_printf("%s", tmp2);
450   }
451 
452   lives_free(tmp1);
453   lives_free(tmp2);
454 
455   return ret;
456 }
457 
458 
update_host_info(weed_plant_t * hinfo)459 void update_host_info(weed_plant_t *hinfo) {
460   // set "host_audio_plugin" in the host_info
461   switch (prefs->audio_player) {
462   case AUD_PLAYER_SOX:
463     weed_set_string_value(hinfo, WEED_LEAF_HOST_AUDIO_PLAYER, AUDIO_PLAYER_SOX);
464     break;
465   case AUD_PLAYER_JACK:
466     weed_set_string_value(hinfo, WEED_LEAF_HOST_AUDIO_PLAYER, AUDIO_PLAYER_JACK);
467     break;
468   case AUD_PLAYER_PULSE:
469     weed_set_string_value(hinfo, WEED_LEAF_HOST_AUDIO_PLAYER, AUDIO_PLAYER_PULSE);
470     break;
471   case AUD_PLAYER_NONE:
472     weed_set_string_value(hinfo, WEED_LEAF_HOST_AUDIO_PLAYER, AUDIO_PLAYER_NONE);
473     break;
474   }
475 }
476 
477 
update_all_host_info(void)478 void update_all_host_info(void) {
479   // update host_info for all loaded filters
480   // we should call this when any of the relevant data changes
481   // (for now it's just the audio player)
482   weed_plant_t *filter, *hinfo, *pinfo;
483   int i;
484   for (i = 0; i < num_weed_filters; i++) {
485     filter = weed_filters[i];
486     hinfo = weed_get_plantptr_value(filter, WEED_LEAF_HOST_INFO, NULL);
487     if (!hinfo) {
488       pinfo = weed_get_plantptr_value(filter, WEED_LEAF_PLUGIN_INFO, NULL);
489       if (pinfo)
490         hinfo = weed_get_plantptr_value(pinfo, WEED_LEAF_HOST_INFO, NULL);
491     }
492     if (hinfo && WEED_PLANT_IS_HOST_INFO(hinfo)) update_host_info(hinfo);
493   }
494 }
495 
496 
get_enabled_channel_inner(weed_plant_t * inst,int which,boolean is_in,boolean audio_only)497 static weed_plant_t *get_enabled_channel_inner(weed_plant_t *inst, int which, boolean is_in, boolean audio_only) {
498   // plant is a filter_instance
499   // "which" starts at 0
500   weed_plant_t **channels = NULL;
501   weed_plant_t *retval, *ctmpl = NULL;
502 
503   int nchans = 3;
504 
505   int i = 0;
506 
507   if (!WEED_PLANT_IS_FILTER_INSTANCE(inst)) return NULL;
508 
509   if (is_in) {
510     channels = weed_instance_get_in_channels(inst, &nchans);
511   } else {
512     channels = weed_instance_get_out_channels(inst, &nchans);
513   }
514 
515   if (!nchans) return NULL;
516 
517   while (1) {
518     if (weed_get_boolean_value(channels[i], WEED_LEAF_DISABLED, NULL) == WEED_FALSE) {
519       if (audio_only) ctmpl = weed_channel_get_template(channels[i]);
520       if (!audio_only || (audio_only && weed_chantmpl_is_audio(ctmpl) == WEED_TRUE)) {
521         which--;
522       }
523     }
524     if (which < 0) break;
525     if (++i >= nchans) {
526       lives_free(channels);
527       return NULL;
528     }
529   }
530   retval = channels[i];
531   lives_freep((void **)&channels);
532   return retval;
533 }
534 
535 
get_enabled_channel(weed_plant_t * inst,int which,boolean is_in)536 weed_plant_t *get_enabled_channel(weed_plant_t *inst, int which, boolean is_in) {
537   // count audio and video channels
538   return get_enabled_channel_inner(inst, which, is_in, FALSE);
539 }
540 
541 
get_enabled_audio_channel(weed_plant_t * inst,int which,boolean is_in)542 weed_plant_t *get_enabled_audio_channel(weed_plant_t *inst, int which, boolean is_in) {
543   // count only audio channels
544   return get_enabled_channel_inner(inst, which, is_in, TRUE);
545 }
546 
547 
get_mandatory_channel(weed_plant_t * filter,int which,boolean is_in)548 weed_plant_t *get_mandatory_channel(weed_plant_t *filter, int which, boolean is_in) {
549   // plant is a filter_class
550   // "which" starts at 0
551   weed_plant_t **ctmpls;
552   weed_plant_t *retval;
553   register int i = 0;
554 
555   if (!WEED_PLANT_IS_FILTER_CLASS(filter)) return NULL;
556 
557   if (is_in) ctmpls = weed_filter_get_in_chantmpls(filter, NULL);
558   else ctmpls = weed_filter_get_out_chantmpls(filter, NULL);
559 
560   if (!ctmpls) return NULL;
561   while (which > -1) {
562     if (!weed_chantmpl_is_optional(ctmpls[i])) which--;
563     i++;
564   }
565   retval = ctmpls[i - 1];
566   lives_free(ctmpls);
567   return retval;
568 }
569 
570 
weed_instance_is_resizer(weed_plant_t * inst)571 LIVES_GLOBAL_INLINE boolean weed_instance_is_resizer(weed_plant_t *inst) {
572   weed_plant_t *ftmpl = weed_instance_get_filter(inst, TRUE);
573   return weed_filter_is_resizer(ftmpl);
574 }
575 
576 
is_audio_channel_in(weed_plant_t * inst,int chnum)577 boolean is_audio_channel_in(weed_plant_t *inst, int chnum) {
578   int nchans = 0;
579   weed_plant_t **in_chans;
580   weed_plant_t *ctmpl;
581 
582   in_chans = weed_instance_get_in_channels(inst, &nchans);
583   if (nchans <= chnum) {
584     lives_freep((void **)&in_chans);
585     return FALSE;
586   }
587 
588   ctmpl = weed_channel_get_template(in_chans[chnum]);
589   lives_free(in_chans);
590 
591   return (weed_chantmpl_is_audio(ctmpl) == WEED_TRUE);
592 }
593 
594 
get_audio_channel_in(weed_plant_t * inst,int achnum)595 weed_plant_t *get_audio_channel_in(weed_plant_t *inst, int achnum) {
596   // get nth audio channel in (not counting video channels)
597   int nchans = 0;
598   weed_plant_t **in_chans;
599   weed_plant_t *ctmpl, *achan;
600 
601   in_chans = weed_instance_get_in_channels(inst, &nchans);
602   if (!in_chans) return NULL;
603 
604   for (register int i = 0; i < nchans; i++) {
605     ctmpl = weed_channel_get_template(in_chans[i]);
606     if (weed_chantmpl_is_audio(ctmpl)) {
607       if (achnum-- == 0) {
608         achan = in_chans[i];
609         lives_free(in_chans);
610         return achan;
611       }
612     }
613   }
614 
615   lives_freep((void **)&in_chans);
616   return NULL;
617 }
618 
619 
has_video_chans_in(weed_plant_t * filter,boolean count_opt)620 boolean has_video_chans_in(weed_plant_t *filter, boolean count_opt) {
621   // TODO: we should probably distinguish between enabled and disabled optional channels
622   int nchans = 0;
623   weed_plant_t **in_ctmpls;
624 
625   in_ctmpls = weed_filter_get_in_chantmpls(filter, &nchans);
626   if (!in_ctmpls) return FALSE;
627 
628   for (register int i = 0; i < nchans; i++) {
629     if (!count_opt && weed_chantmpl_is_optional(in_ctmpls[i])) continue;
630     if (weed_chantmpl_is_audio(in_ctmpls[i])) continue;
631     lives_free(in_ctmpls);
632     return TRUE;
633   }
634   lives_freep((void **)&in_ctmpls);
635 
636   return FALSE;
637 }
638 
639 
has_audio_chans_in(weed_plant_t * filter,boolean count_opt)640 boolean has_audio_chans_in(weed_plant_t *filter, boolean count_opt) {
641   int nchans = 0;
642   weed_plant_t **in_ctmpls = weed_filter_get_in_chantmpls(filter, &nchans);
643   if (!in_ctmpls) return FALSE;
644   for (int i = 0; i < nchans; i++) {
645     if (!count_opt && weed_chantmpl_is_optional(in_ctmpls[i])) continue;
646     if (weed_chantmpl_is_audio(in_ctmpls[i])) {
647       lives_free(in_ctmpls);
648       return TRUE;
649     }
650   }
651   lives_freep((void **)&in_ctmpls);
652   return FALSE;
653 }
654 
655 
is_audio_channel_out(weed_plant_t * inst,int chnum)656 boolean is_audio_channel_out(weed_plant_t *inst, int chnum) {
657   weed_plant_t **out_chans;
658   weed_plant_t *ctmpl;
659   int nchans = 0;
660 
661   out_chans = weed_instance_get_in_channels(inst, &nchans);
662   if (nchans <= chnum) {
663     lives_freep((void **)&out_chans);
664     return FALSE;
665   }
666   ctmpl = weed_channel_get_template(out_chans[chnum]);
667   lives_free(out_chans);
668 
669   if (weed_chantmpl_is_audio(ctmpl) == WEED_TRUE) {
670     return TRUE;
671   }
672   return FALSE;
673 }
674 
675 
has_video_chans_out(weed_plant_t * filter,boolean count_opt)676 boolean has_video_chans_out(weed_plant_t *filter, boolean count_opt) {
677   weed_plant_t **out_ctmpls;
678   int nchans = 0;
679 
680   out_ctmpls = weed_filter_get_out_chantmpls(filter, &nchans);
681   if (!out_ctmpls) return FALSE;
682 
683   for (int i = 0; i < nchans; i++) {
684     if (!count_opt && weed_chantmpl_is_optional(out_ctmpls[i])) continue;
685     if (weed_chantmpl_is_audio(out_ctmpls[i]) == WEED_TRUE) continue;
686     lives_free(out_ctmpls);
687     return TRUE;
688   }
689   lives_freep((void **)&out_ctmpls);
690 
691   return FALSE;
692 }
693 
694 
has_audio_chans_out(weed_plant_t * filter,boolean count_opt)695 boolean has_audio_chans_out(weed_plant_t *filter, boolean count_opt) {
696   weed_plant_t **out_ctmpls;
697   int nchans = 0;
698 
699   out_ctmpls = weed_filter_get_out_chantmpls(filter, &nchans);
700   if (!out_ctmpls) return FALSE;
701 
702   for (int i = 0; i < nchans; i++) {
703     if (!count_opt && weed_chantmpl_is_optional(out_ctmpls[i])) continue;
704     if (weed_chantmpl_is_audio(out_ctmpls[i]) == WEED_FALSE) continue;
705     lives_free(out_ctmpls);
706     return TRUE;
707   }
708   lives_freep((void **)&out_ctmpls);
709 
710   return FALSE;
711 }
712 
713 
is_pure_audio(weed_plant_t * plant,boolean count_opt)714 boolean is_pure_audio(weed_plant_t *plant, boolean count_opt) {
715   weed_plant_t *filter = plant;
716   if (WEED_PLANT_IS_FILTER_INSTANCE(plant)) filter = weed_instance_get_filter(plant, FALSE);
717 
718   if ((has_audio_chans_in(filter, count_opt) || has_audio_chans_out(filter, count_opt)) &&
719       !has_video_chans_in(filter, count_opt) && !has_video_chans_out(filter, count_opt)) return TRUE;
720   return FALSE;
721 }
722 
723 
weed_parameter_has_variable_elements_strict(weed_plant_t * inst,weed_plant_t * ptmpl)724 boolean weed_parameter_has_variable_elements_strict(weed_plant_t *inst, weed_plant_t *ptmpl) {
725   /** see if param has variable elements, using the strictest check */
726   weed_plant_t **chans, *ctmpl;
727   int flags = weed_get_int_value(ptmpl, WEED_LEAF_FLAGS, NULL);
728   int nchans;
729 
730   if (flags & WEED_PARAMETER_VARIABLE_SIZE) return TRUE;
731 
732   if (!inst) return FALSE;
733 
734   if (!(flags & WEED_PARAMETER_VALUE_PER_CHANNEL)) return FALSE;
735 
736   if ((nchans = weed_leaf_num_elements(inst, WEED_LEAF_IN_CHANNELS)) == 0)
737     return FALSE;
738 
739   chans = weed_get_plantptr_array(inst, WEED_LEAF_IN_CHANNELS, NULL);
740 
741   for (int i = 0; i < nchans; i++) {
742     if (weed_get_boolean_value(chans[i], WEED_LEAF_DISABLED, NULL) == WEED_TRUE) continue; //ignore disabled channels
743     ctmpl = weed_get_plantptr_value(chans[i], WEED_LEAF_TEMPLATE, NULL);
744     if (weed_plant_has_leaf(ctmpl, WEED_LEAF_MAX_REPEATS) && weed_get_int_value(ctmpl, WEED_LEAF_MAX_REPEATS, NULL) != 1) {
745       lives_free(chans);
746       return TRUE;
747     }
748   }
749 
750   lives_freep((void **)&chans);
751   return FALSE;
752 }
753 
754 
755 /**
756    @brief create an effect (filter) map
757 
758    here we create an effect map which defines the order in which effects are applied to a frame stack
759    this is done during recording, the keymap is from mainw->rte which is a bitmap of effect keys
760    keys are applied here from smallest (ctrl-1) to largest (virtual key ctrl-FX_KEYS_MAX_VIRTUAL)
761 
762    this is transformed into filter_map which holds init_events
763    these pointers are then stored in a filter_map event
764 
765    what we actually point to are the init_events for the effects. The init_events are stored when we
766    init an effect
767 
768    during rendering we read the filter_map event, and retrieve the new key, which is at that time
769    held in the
770    WEED_LEAF_HOST_TAG property of the init_event, and we apply our effects
771    (which are then bound to virtual keys >=FX_KEYS_MAX_VIRTUAL)
772 
773    [note] that we can do cool things, like mapping the same instance multiple times (though it will always
774    apply itself to the same in/out tracks
775 
776    we don't need to worry about free()ing init_events, since they will be free'd
777    when the instance is deinited
778 */
create_filter_map(uint64_t rteval)779 static void create_filter_map(uint64_t rteval) {
780   int count = 0, i;
781   weed_plant_t *inst;
782 
783   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
784     if (rteval & (GU641 << i) && (inst = weed_instance_obtain(i, key_modes[i])) != NULL) {
785       if (enabled_in_channels(inst, FALSE) > 0) {
786         filter_map[count++] = init_events[i];
787       }
788       weed_instance_unref(inst);
789     }
790   }
791   filter_map[count] = NULL; ///< marks the end of the effect map
792 }
793 
794 
795 /**
796    @brief add effect_deinit events to an event_list
797 
798     during real time recording we use the "keys" 0 -> FX_KEYS_MAX_VIRTUAL
799     here we add effect_deinit events to an event_list
800 
801     @see deinit_render_effects()
802 */
add_filter_deinit_events(weed_plant_t * event_list)803 weed_plant_t *add_filter_deinit_events(weed_plant_t *event_list) {
804   // should be called with mainw->event_list_mutex unlocked !
805   int i;
806   boolean needs_filter_map = FALSE;
807   weed_timecode_t last_tc = 0;
808 
809   if (event_list) last_tc = get_event_timecode(get_last_event(event_list));
810 
811   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
812     if (init_events[i]) {
813       event_list = append_filter_deinit_event(event_list, last_tc, init_events[i], pchains[i]);
814       init_events[i] = NULL;
815       // no freep !
816       if (pchains[i]) lives_free(pchains[i]);
817       pchains[i] = NULL;
818       needs_filter_map = TRUE;
819     }
820   }
821 
822   /// add an empty filter_map event (in case more frames are added)
823   create_filter_map(mainw->rte); ///< we create filter_map event_t * array with ordered effects
824   if (needs_filter_map) {
825     pthread_mutex_lock(&mainw->event_list_mutex);
826     event_list = append_filter_map_event(event_list, last_tc, filter_map);
827     pthread_mutex_unlock(&mainw->event_list_mutex);
828   }
829   return event_list;
830 }
831 
832 
833 /**
834    @brief add init events for every effect which is switched on
835 
836     during rendering we use the "keys" FX_KEYS_MAX_VIRTUAL -> FX_KEYS_MAX
837     here we are about to start playback, and we add init events for every effect which is switched on
838     we add the init events with a timecode of 0 */
add_filter_init_events(weed_plant_t * event_list,weed_timecode_t tc)839 weed_plant_t *add_filter_init_events(weed_plant_t *event_list, weed_timecode_t tc) {
840   int i;
841   weed_plant_t *inst;
842   int fx_idx, ntracks;
843 
844   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
845     if ((inst = weed_instance_obtain(i, key_modes[i])) != NULL) {
846       if (enabled_in_channels(inst, FALSE) > 0) {
847         event_list = append_filter_init_event(event_list, tc, (fx_idx = key_to_fx[i][key_modes[i]]), -1, i, inst);
848         init_events[i] = get_last_event(event_list);
849         ntracks = weed_leaf_num_elements(init_events[i], WEED_LEAF_IN_TRACKS);
850         pchains[i] = filter_init_add_pchanges(event_list, inst, init_events[i], ntracks, 0);
851       }
852       weed_instance_unref(inst);
853     }
854   }
855   /// add an empty filter_map event (in case more frames are added)
856   create_filter_map(mainw->rte); /// we create filter_map event_t * array with ordered effects
857   if (filter_map[0]) event_list = append_filter_map_event(event_list, tc, filter_map);
858   return event_list;
859 }
860 
861 
862 /**
863     @brief check palette vs. palette list
864 
865     check if palette is in the palette_list
866     if not, return next best palette to use, using a heuristic method
867     num_palettes is the size of the palette list */
best_palette_match(int * palette_list,int num_palettes,int palette)868 int best_palette_match(int *palette_list, int num_palettes, int palette) {
869   int best_palette = palette_list[0];
870   boolean has_alpha, is_rgb, is_alpha, is_yuv, mismatch;
871 
872   if (palette == best_palette) return palette;
873 
874   has_alpha = weed_palette_has_alpha(palette);
875   is_rgb = weed_palette_is_rgb(palette);
876   is_alpha = weed_palette_is_alpha(palette);
877   is_yuv = !is_alpha && !is_rgb;
878 
879   for (int i = 0; (num_palettes > 0 && i < num_palettes) || (num_palettes <= 0 && palette_list[i] != WEED_PALETTE_END); i++) {
880     if (palette_list[i] == palette) {
881       /// exact match - return it
882       return palette;
883     }
884 
885     if ((weed_palette_is_rgb(best_palette) && !is_rgb) || (weed_palette_is_alpha(best_palette) && !is_alpha)
886         || (weed_palette_is_yuv(best_palette) && !is_yuv)) mismatch = TRUE;
887     else mismatch = FALSE;
888 
889     switch (palette_list[i]) {
890     case WEED_PALETTE_RGB24:
891     case WEED_PALETTE_BGR24:
892       if (palette == WEED_PALETTE_RGB24 || palette == WEED_PALETTE_BGR24) return palette_list[i];
893 
894       if ((is_rgb || mismatch) && (!weed_palette_has_alpha(palette) || best_palette == WEED_PALETTE_RGBAFLOAT ||
895                                    best_palette == WEED_PALETTE_RGBFLOAT)
896           && !(best_palette == WEED_PALETTE_RGB24 || best_palette == WEED_PALETTE_BGR24))
897         best_palette = palette_list[i];
898       break;
899     case WEED_PALETTE_RGBA32:
900     case WEED_PALETTE_BGRA32:
901       if (palette == WEED_PALETTE_RGBA32 || palette == WEED_PALETTE_BGRA32) best_palette = palette_list[i];
902     case WEED_PALETTE_ARGB32:
903       if ((is_rgb || mismatch) && (has_alpha || best_palette == WEED_PALETTE_RGBFLOAT
904                                    || best_palette == WEED_PALETTE_RGBAFLOAT)
905           && best_palette != WEED_PALETTE_RGBA32
906           && !(palette == WEED_PALETTE_ARGB32 && best_palette == WEED_PALETTE_BGRA32))
907         best_palette = palette_list[i];
908       break;
909     case WEED_PALETTE_RGBAFLOAT:
910       if ((is_rgb || mismatch) && best_palette == WEED_PALETTE_RGBFLOAT && has_alpha)
911         best_palette = WEED_PALETTE_RGBAFLOAT;
912       break;
913     case WEED_PALETTE_RGBFLOAT:
914       if ((is_rgb || mismatch) && best_palette == WEED_PALETTE_RGBAFLOAT && !has_alpha)
915         best_palette = WEED_PALETTE_RGBFLOAT;
916       break;
917     // yuv
918     case WEED_PALETTE_YUVA4444P:
919       if (is_yuv && has_alpha) return WEED_PALETTE_YUVA4444P;
920       if ((mismatch || is_yuv)
921           && (has_alpha || (best_palette != WEED_PALETTE_YUV444P && best_palette != WEED_PALETTE_YUV888)))
922         best_palette = WEED_PALETTE_YUVA4444P;
923       break;
924     case WEED_PALETTE_YUV444P:
925       if (is_yuv && !has_alpha) return WEED_PALETTE_YUV444P;
926       if ((mismatch || is_yuv)
927           && (!has_alpha || (best_palette != WEED_PALETTE_YUVA4444P && best_palette != WEED_PALETTE_YUVA8888)))
928         best_palette = WEED_PALETTE_YUV444P;
929       break;
930     case WEED_PALETTE_YUVA8888:
931       if ((mismatch || is_yuv) && (has_alpha || (best_palette != WEED_PALETTE_YUV444P && best_palette != WEED_PALETTE_YUV888)))
932         best_palette = WEED_PALETTE_YUVA8888;
933       break;
934     case WEED_PALETTE_YUV888:
935       if ((mismatch || is_yuv)
936           && (!has_alpha || (best_palette != WEED_PALETTE_YUVA4444P && best_palette != WEED_PALETTE_YUVA8888)))
937         best_palette = WEED_PALETTE_YUV888;
938       break;
939     case WEED_PALETTE_YUV422P:
940     case WEED_PALETTE_UYVY:
941     case WEED_PALETTE_YUYV:
942       if ((mismatch || is_yuv) && (best_palette != WEED_PALETTE_YUVA4444P && best_palette != WEED_PALETTE_YUV444P
943                                    && best_palette != WEED_PALETTE_YUVA8888 && best_palette != WEED_PALETTE_YUV888))
944         best_palette = palette_list[i];
945       break;
946     case WEED_PALETTE_YUV420P:
947     case WEED_PALETTE_YVU420P:
948       if ((mismatch || is_yuv) && (best_palette == WEED_PALETTE_YUV411 || weed_palette_is_alpha(best_palette)))
949         best_palette = palette_list[i];
950       break;
951     default:
952       if (weed_palette_is_alpha(best_palette)) {
953         if (palette_list[i] == WEED_PALETTE_YUV411 && !is_alpha) best_palette = WEED_PALETTE_A8;
954         if (palette_list[i] == WEED_PALETTE_A8) best_palette = WEED_PALETTE_A8;
955         if (palette_list[i] == WEED_PALETTE_A1 && (best_palette != WEED_PALETTE_A8))
956           best_palette = WEED_PALETTE_A1;
957       }
958       break;
959     }
960   }
961 
962 #ifdef DEBUG_PALETTES
963   lives_printerr("Debug: best palette for %d is %d\n", palette, best_palette);
964 #endif
965 
966   return best_palette;
967 }
968 
969 
get_fixed_channel_size(weed_plant_t * template,int * width,int * height)970 static boolean get_fixed_channel_size(weed_plant_t *template, int *width, int *height) {
971   weed_size_t nwidths = weed_leaf_num_elements(template, WEED_LEAF_WIDTH);
972   weed_size_t nheights = weed_leaf_num_elements(template, WEED_LEAF_HEIGHT);
973   int *heights, *widths;
974   int defined = 0;
975   int oheight = 0, owidth = 0;
976   int i;
977   if (!width || nwidths == 0) defined++;
978   else {
979     if (nwidths == 1) {
980       *width = weed_get_int_value(template, WEED_LEAF_WIDTH, NULL);
981       defined++;
982     }
983   }
984 
985   if (!height || nheights == 0) defined++;
986   else {
987     if (nheights == 1) {
988       *height = weed_get_int_value(template, WEED_LEAF_HEIGHT, NULL);
989       defined++;
990     }
991   }
992   if (defined == 2) return TRUE;
993 
994   /// we have dealt with n == 0 and n == 1, now we should have pairs
995   if (nwidths != nheights) {
996     /// spec error
997     return FALSE;
998   }
999 
1000   widths = weed_get_int_array(template, WEED_LEAF_WIDTH, NULL);
1001   heights = weed_get_int_array(template, WEED_LEAF_HEIGHT, NULL);
1002 
1003   if (width) owidth = *width;
1004   if (height) oheight = *height;
1005 
1006   for (i = 0; i < nwidths; i++) {
1007     defined = 0;
1008     // these should be in ascending order, so we'll just quit when both values are greater
1009     if (!width) defined++;
1010     else {
1011       *width = widths[i];
1012       if (*width >= owidth) defined++;
1013     }
1014     if (!height) defined++;
1015     else {
1016       *height = heights[i];
1017       if (*height >= oheight) defined++;
1018     }
1019     if (defined == 2) return TRUE;
1020   }
1021   return FALSE;
1022 }
1023 
1024 
1025 #define MIN_CHAN_WIDTH 4
1026 #define MIN_CHAN_HEIGHT 4
1027 
1028 #define MAX_CHAN_WIDTH 16384
1029 #define MAX_CHAN_HEIGHT 16384
1030 
1031 /**
1032     @brief set the size of a video filter channel as near as possible to width (macropixels) * height
1033 
1034     We will comply with any restrictions set up for the channel, ie.
1035     (fixed width(s), fixed height(s), hstep, vstep, maxwidth, maxheight, minwidth, minheight)
1036 
1037     if there are user defined defaults for the size (e.g it's the out channel of a generator) then we use
1038     those, provided this doesn't confilct with the channel's fixed size requirements
1039 
1040     we take the restrictions from the filter class owning the channel template from which the channel was modelled,
1041     unless the filter flag WEED_FILTER_CHANNEL_SIZES_MAY_VARY, in which case the filter defaults may be
1042     overriden by values in the channel template
1043 
1044     function is called with dummy values when we first create the channels, and then with real values before we process a frame
1045 */
set_channel_size(weed_plant_t * filter,weed_plant_t * channel,int width,int height)1046 static void set_channel_size(weed_plant_t *filter, weed_plant_t *channel, int width, int height) {
1047   weed_plant_t *chantmpl = weed_channel_get_template(channel);
1048   boolean check_ctmpl = FALSE;
1049   int max, min;
1050   int filter_flags = weed_filter_get_flags(filter);
1051 
1052   if (width < MIN_CHAN_WIDTH) width = MIN_CHAN_WIDTH;
1053   if (width > MAX_CHAN_WIDTH) width = MAX_CHAN_WIDTH;
1054   if (height < MIN_CHAN_HEIGHT) height = MIN_CHAN_HEIGHT;
1055   if (height > MAX_CHAN_HEIGHT) height = MAX_CHAN_HEIGHT;
1056 
1057   if (filter_flags & WEED_FILTER_CHANNEL_SIZES_MAY_VARY) {
1058     check_ctmpl = TRUE;
1059   }
1060 
1061   if (check_ctmpl && weed_plant_has_leaf(chantmpl, WEED_LEAF_WIDTH)) {
1062     get_fixed_channel_size(chantmpl, &width, &height);
1063   } else {
1064     if (weed_plant_has_leaf(filter, WEED_LEAF_WIDTH)) {
1065       get_fixed_channel_size(filter, &width, &height);
1066     } else {
1067       if (weed_plant_has_leaf(chantmpl, WEED_LEAF_HOST_WIDTH)) {
1068         width = weed_get_int_value(chantmpl, WEED_LEAF_HOST_WIDTH, NULL);
1069       }
1070       if (weed_plant_has_leaf(filter, WEED_LEAF_HSTEP)) {
1071         width = ALIGN_CEIL(width, weed_get_int_value(filter, WEED_LEAF_HSTEP, NULL));
1072       }
1073       if (width < MIN_CHAN_WIDTH) width = MIN_CHAN_WIDTH;
1074       if (width > MAX_CHAN_WIDTH) width = MAX_CHAN_WIDTH;
1075       if (check_ctmpl && weed_plant_has_leaf(chantmpl, WEED_LEAF_MAXWIDTH)) {
1076         max = weed_get_int_value(chantmpl, WEED_LEAF_MAXWIDTH, NULL);
1077         if (max > 0 && width > max) width = max;
1078       } else if (weed_plant_has_leaf(filter, WEED_LEAF_MAXWIDTH)) {
1079         max = weed_get_int_value(filter, WEED_LEAF_MAXWIDTH, NULL);
1080         if (width > max) width = max;
1081       }
1082       if (check_ctmpl && weed_plant_has_leaf(chantmpl, WEED_LEAF_MINWIDTH)) {
1083         min = weed_get_int_value(chantmpl, WEED_LEAF_MINWIDTH, NULL);
1084         if (min > 0 && width < min) width = min;
1085       } else if (weed_plant_has_leaf(filter, WEED_LEAF_MINWIDTH)) {
1086         min = weed_get_int_value(filter, WEED_LEAF_MINWIDTH, NULL);
1087         if (width < min) width = min;
1088       }
1089     }
1090   }
1091 
1092   if (width < MIN_CHAN_WIDTH) width = MIN_CHAN_WIDTH;
1093   if (width > MAX_CHAN_WIDTH) width = MAX_CHAN_WIDTH;
1094 
1095   width = (width >> 1) << 1;
1096   weed_set_int_value(channel, WEED_LEAF_WIDTH, width);
1097 
1098   if (check_ctmpl && weed_plant_has_leaf(chantmpl, WEED_LEAF_HEIGHT)) {
1099     get_fixed_channel_size(chantmpl, &width, &height);
1100   } else {
1101     if (weed_plant_has_leaf(filter, WEED_LEAF_HEIGHT)) {
1102       get_fixed_channel_size(filter, &width, &height);
1103     } else {
1104       if (weed_plant_has_leaf(chantmpl, WEED_LEAF_HOST_HEIGHT)) {
1105         height = weed_get_int_value(chantmpl, WEED_LEAF_HOST_HEIGHT, NULL);
1106       }
1107       if (weed_plant_has_leaf(filter, WEED_LEAF_VSTEP)) {
1108         height = ALIGN_CEIL(height, weed_get_int_value(filter, WEED_LEAF_VSTEP, NULL));
1109       }
1110       if (height < MIN_CHAN_HEIGHT) height = MIN_CHAN_HEIGHT;
1111       if (height > MAX_CHAN_HEIGHT) height = MAX_CHAN_HEIGHT;
1112       if (check_ctmpl && weed_plant_has_leaf(chantmpl, WEED_LEAF_MAXHEIGHT)) {
1113         max = weed_get_int_value(chantmpl, WEED_LEAF_MAXHEIGHT, NULL);
1114         if (max > 0 && height > max) height = max;
1115       } else if (weed_plant_has_leaf(filter, WEED_LEAF_MAXHEIGHT)) {
1116         max = weed_get_int_value(filter, WEED_LEAF_MAXHEIGHT, NULL);
1117         if (height > max) height = max;
1118       }
1119       if (check_ctmpl && weed_plant_has_leaf(chantmpl, WEED_LEAF_MINHEIGHT)) {
1120         min = weed_get_int_value(chantmpl, WEED_LEAF_MINHEIGHT, NULL);
1121         if (min > 0 && height < min) height = min;
1122       } else if (weed_plant_has_leaf(filter, WEED_LEAF_MINHEIGHT)) {
1123         min = weed_get_int_value(filter, WEED_LEAF_MINHEIGHT, NULL);
1124         if (height < min) height = min;
1125       }
1126     }
1127   }
1128 
1129   if (height < MIN_CHAN_HEIGHT) height = MIN_CHAN_HEIGHT;
1130   if (height > MAX_CHAN_HEIGHT) height = MAX_CHAN_HEIGHT;
1131   height = (height >> 1) << 1;
1132   weed_set_int_value(channel, WEED_LEAF_HEIGHT, height);
1133 }
1134 
1135 
weed_flagset_array_count(weed_plant_t ** array,boolean set_readonly)1136 int weed_flagset_array_count(weed_plant_t **array, boolean set_readonly) {
1137   register int i;
1138   for (i = 0; array[i]; i++) {
1139     if (set_readonly) weed_add_plant_flags(array[i], WEED_FLAG_IMMUTABLE | WEED_FLAG_UNDELETABLE, "plugin_");
1140   }
1141   return i;
1142 }
1143 
1144 
1145 /// change directory to plugin installation dir so it can find any data files
1146 ///
1147 /// returns copy of current directory (before directory change) which should be freed after use
cd_to_plugin_dir(weed_plant_t * filter)1148 char *cd_to_plugin_dir(weed_plant_t *filter) {
1149   weed_plant_t *plugin_info;
1150   char *ppath;
1151   char *ret;
1152   if (WEED_PLANT_IS_PLUGIN_INFO(filter)) plugin_info = filter; // on shutdown the filters are freed.
1153   else plugin_info = weed_get_plantptr_value(filter, WEED_LEAF_PLUGIN_INFO, NULL);
1154   ppath = weed_get_string_value(plugin_info, WEED_LEAF_HOST_PLUGIN_PATH, NULL);
1155   ret = lives_get_current_dir();
1156   // allow this to fail -it's not that important - it just means any plugin data files wont be found
1157   // besides, we dont want to show warnings at 50 fps
1158   lives_chdir(ppath, TRUE);
1159   lives_free(ppath);
1160   return ret;
1161 }
1162 
1163 
get_next_compound_inst(weed_plant_t * inst)1164 LIVES_GLOBAL_INLINE weed_plant_t *get_next_compound_inst(weed_plant_t *inst) {
1165   return weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, NULL);
1166 }
1167 
1168 
weed_reinit_effect(weed_plant_t * inst,boolean reinit_compound)1169 lives_filter_error_t weed_reinit_effect(weed_plant_t *inst, boolean reinit_compound) {
1170   // call with filter_mutex unlocked
1171   weed_plant_t *filter, *orig_inst = inst;
1172   lives_filter_error_t filter_error = FILTER_SUCCESS;
1173   char *cwd;
1174   boolean deinit_first = FALSE;
1175   weed_error_t retval;
1176   int key = -1;
1177 
1178   weed_instance_ref(inst);
1179 
1180   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
1181 
1182   if (key != -1) {
1183     weed_plant_t *gui;
1184     filter_mutex_lock(key);
1185     gui = weed_instance_get_gui(inst, FALSE);
1186     if (weed_get_int_value(gui, WEED_LEAF_EASE_OUT, NULL) > 0) {
1187       // plugin is easing, so we need to deinit it
1188       uint64_t new_rte = GU641 << (key);
1189       if (mainw->rte & new_rte) mainw->rte ^= new_rte;
1190       // WEED_LEAF_EASE_OUT exists, so it'll get deinited right away
1191       weed_deinit_effect(key);
1192       weed_instance_unref(inst);
1193       filter_mutex_unlock(key);
1194       return FILTER_ERROR_COULD_NOT_REINIT;
1195     }
1196   }
1197 
1198   mainw->blend_palette = WEED_PALETTE_END;
1199 
1200 reinit:
1201 
1202   filter = weed_instance_get_filter(inst, FALSE);
1203 
1204   if (weed_get_boolean_value(inst, WEED_LEAF_HOST_INITED, NULL) == WEED_TRUE) deinit_first = TRUE;
1205 
1206   if (deinit_first) {
1207     weed_call_deinit_func(inst);
1208   }
1209 
1210   if (weed_plant_has_leaf(filter, WEED_LEAF_INIT_FUNC)) {
1211     weed_init_f init_func = (weed_init_f)weed_get_funcptr_value(filter, WEED_LEAF_INIT_FUNC, NULL);
1212     cwd = cd_to_plugin_dir(filter);
1213     if (init_func) {
1214       lives_rfx_t *rfx;
1215       retval = (*init_func)(inst);
1216       if (fx_dialog[1]
1217           || (mainw->multitrack &&  mainw->multitrack->current_rfx
1218               && mainw->multitrack->poly_state == POLY_PARAMS)) {
1219         // redraw GUI if necessary
1220         if (fx_dialog[1]) rfx = fx_dialog[1]->rfx;
1221         else rfx = mainw->multitrack->current_rfx;
1222         if (rfx->source_type == LIVES_RFX_SOURCE_WEED && rfx->source == inst) {
1223           // update any text params with focus
1224           if (mainw->textwidget_focus && LIVES_IS_WIDGET_OBJECT(mainw->textwidget_focus)) {
1225             // make sure text widgets are updated if they activate the default
1226             LiVESWidget *textwidget =
1227               (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mainw->textwidget_focus), TEXTWIDGET_KEY);
1228             weed_set_boolean_value(inst, WEED_LEAF_HOST_REINITING, WEED_TRUE);
1229             after_param_text_changed(textwidget, rfx);
1230             weed_leaf_delete(inst, WEED_LEAF_HOST_REINITING);
1231             mainw->textwidget_focus = NULL;
1232           }
1233 
1234           // do updates from "gui"
1235           //rfx_params_free(rfx);
1236           //lives_freep((void **)&rfx->params);
1237           if (rfx->num_params > 0 && !rfx->params)
1238             rfx->params = weed_params_to_rfx(rfx->num_params, inst, FALSE);
1239           if (fx_dialog[1]) {
1240             int keyw = fx_dialog[1]->key;
1241             int modew = fx_dialog[1]->mode;
1242             update_widget_vis(NULL, keyw, modew);
1243           } else update_widget_vis(rfx, -1, -1);
1244           filter_error = FILTER_INFO_REDRAWN;
1245         }
1246       }
1247 
1248       if (retval != WEED_SUCCESS) {
1249         weed_instance_unref(orig_inst);
1250         lives_chdir(cwd, FALSE);
1251         lives_free(cwd);
1252         if (key != -1) filter_mutex_unlock(key);
1253         return FILTER_ERROR_COULD_NOT_REINIT;
1254       }
1255 
1256       // need to set this before calling deinit
1257       weed_set_boolean_value(inst, WEED_LEAF_HOST_INITED, WEED_TRUE);
1258     }
1259     if (!deinit_first) {
1260       weed_call_deinit_func(inst);
1261     }
1262 
1263     lives_chdir(cwd, FALSE);
1264     lives_free(cwd);
1265     if (filter_error != FILTER_INFO_REDRAWN) filter_error = FILTER_INFO_REINITED;
1266   }
1267 
1268   if (deinit_first) {
1269     weed_set_boolean_value(inst, WEED_LEAF_HOST_INITED, WEED_TRUE);
1270     weed_set_boolean_value(inst, WEED_LEAF_HOST_UNUSED, WEED_TRUE);
1271   } else {
1272     weed_set_boolean_value(inst, WEED_LEAF_HOST_INITED, WEED_FALSE);
1273     weed_leaf_delete(inst, WEED_LEAF_HOST_UNUSED);
1274   }
1275 
1276   if (reinit_compound) {
1277     inst = get_next_compound_inst(inst);
1278     if (inst) goto reinit;
1279   }
1280 
1281   if (key != -1) filter_mutex_unlock(key);
1282   weed_instance_unref(orig_inst);
1283   mainw->blend_palette = WEED_PALETTE_END;
1284   return filter_error;
1285 }
1286 
1287 
weed_reinit_all(void)1288 void weed_reinit_all(void) {
1289   // reinit all effects on playback start
1290   lives_filter_error_t retval;
1291   weed_plant_t *instance, *last_inst, *next_inst;
1292   register int i;
1293 
1294   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
1295     if (rte_key_valid(i + 1, TRUE)) {
1296       if (rte_key_is_enabled(1 + i)) {
1297         mainw->osc_block = TRUE;
1298         if ((instance = weed_instance_obtain(i, key_modes[i])) == NULL) {
1299           mainw->osc_block = FALSE;
1300           continue;
1301         }
1302         last_inst = instance;
1303 
1304         // ignore video generators
1305         while ((next_inst = get_next_compound_inst(last_inst)) != NULL) last_inst = next_inst;
1306         if (enabled_in_channels(instance, FALSE) == 0 && enabled_out_channels(last_inst, FALSE) > 0 &&
1307             !is_pure_audio(last_inst, FALSE)) {
1308           weed_instance_unref(instance);
1309           continue;
1310         }
1311         if ((retval = weed_reinit_effect(instance, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
1312           weed_instance_unref(instance);
1313           continue;
1314         }
1315         weed_instance_unref(instance);
1316       }
1317     }
1318   }
1319 
1320   mainw->osc_block = FALSE;
1321 }
1322 
1323 
thread_process_func(void * arg)1324 static void *thread_process_func(void *arg) {
1325   struct _procvals *procvals = (struct _procvals *)arg;
1326   procvals->ret = (*procvals->procfunc)(procvals->inst, procvals->tc);
1327   return NULL;
1328 }
1329 
1330 
1331 #define SLICE_ALIGN 2
1332 
process_func_threaded(weed_plant_t * inst,weed_timecode_t tc)1333 static lives_filter_error_t process_func_threaded(weed_plant_t *inst, weed_timecode_t tc) {
1334   // split output(s) into horizontal slices
1335   struct _procvals *procvals;
1336   lives_thread_t *dthreads = NULL;
1337   weed_plant_t **xinst = NULL;
1338   weed_plant_t **xchannels, *xchan;
1339   weed_plant_t *filter = weed_instance_get_filter(inst, FALSE);
1340   weed_error_t retval;
1341   int nchannels;
1342   int pal;
1343   double vrt;
1344 
1345   weed_plant_t **out_channels = weed_get_plantptr_array_counted(inst, WEED_LEAF_OUT_CHANNELS, &nchannels);
1346   boolean plugin_invalid = FALSE;
1347   boolean filter_invalid = FALSE;
1348   boolean needs_reinit = FALSE;
1349 
1350   int vstep = SLICE_ALIGN, minh;
1351   int slices, slices_per_thread, to_use;
1352   int heights[2], *xheights;
1353   int offset = 0;
1354   int dheight, height, xheight = 0, cheight;
1355   int nthreads = 0;
1356 
1357   int i, j;
1358 
1359   weed_process_f process_func = (weed_process_f)weed_get_funcptr_value(filter, WEED_LEAF_PROCESS_FUNC, NULL);
1360 
1361   filter = weed_instance_get_filter(inst, TRUE);
1362   if (weed_plant_has_leaf(filter, WEED_LEAF_VSTEP)) {
1363     minh = weed_get_int_value(filter, WEED_LEAF_VSTEP, NULL);
1364     if (minh > vstep) vstep = minh;
1365   }
1366 
1367   for (i = 0; i < nchannels; i++) {
1368     /// min height for slices (in all planes) is SLICE_ALIGN, unless an out channel has a larger vstep set
1369     height = weed_channel_get_height(out_channels[i]);
1370     pal = weed_channel_get_palette(out_channels[i]);
1371 
1372     for (j = 0; j < weed_palette_get_nplanes(pal); j++) {
1373       vrt = weed_palette_get_plane_ratio_vertical(pal, j);
1374       cheight = height * vrt;
1375       if (xheight == 0 || cheight < xheight) xheight = cheight;
1376     }
1377   }
1378 
1379   if (xheight % vstep != 0) return FILTER_ERROR_DONT_THREAD;
1380 
1381   // slices = min height / step
1382   slices = xheight / vstep;
1383   slices_per_thread = ALIGN_CEIL(slices, prefs->nfx_threads) / prefs->nfx_threads;
1384 
1385   to_use = ALIGN_CEIL(slices, slices_per_thread) / slices_per_thread;
1386   if (--to_use < 1) return FILTER_ERROR_DONT_THREAD;
1387 
1388   procvals = (struct _procvals *)lives_calloc(to_use, sizeof(struct _procvals));
1389   procvals->ret = WEED_SUCCESS;
1390   xinst = (weed_plant_t **)lives_calloc(to_use, sizeof(weed_plant_t *));
1391   dthreads = (lives_thread_t *)lives_calloc(to_use - 1, sizeof(lives_thread_t));
1392 
1393   for (i = 0; i < nchannels; i++) {
1394     heights[1] = height = weed_get_int_value(out_channels[i], WEED_LEAF_HEIGHT, NULL);
1395     slices = height / vstep;
1396     slices_per_thread = CEIL((double)slices / (double)to_use, 1.);
1397     dheight = slices_per_thread * vstep;
1398     heights[0] = dheight;
1399     weed_set_int_value(out_channels[i], WEED_LEAF_OFFSET, 0);
1400     weed_set_int_array(out_channels[i], WEED_LEAF_HEIGHT, 2, heights);
1401   }
1402 
1403   for (j = 0; j < to_use; j++) {
1404     // each thread needs its own copy of the output channels, so it can have its own WEED_LEAF_OFFSET and WEED_LEAF_HEIGHT
1405     // therefore it also needs its own copy of inst
1406     // but note that WEED_LEAF_PIXEL_DATA always points to the same memory buffer(s)
1407     // this is good also because it avoids concurrency problems with updating inst leaves
1408     // one of the threads (in this case the last one) will get the original inst so it can update values
1409     if (j < to_use - 1) {
1410       pthread_mutex_lock(&mainw->instance_ref_mutex);
1411       xinst[j] = weed_plant_copy(inst);
1412       pthread_mutex_unlock(&mainw->instance_ref_mutex);
1413       xchannels = (weed_plant_t **)lives_calloc(nchannels, sizeof(weed_plant_t *));
1414     } else {
1415       xinst[j] = inst;
1416       xchannels = NULL;
1417     }
1418 
1419     for (i = 0; i < nchannels; i++) {
1420       if (j < to_use - 1) {
1421         xchan = xchannels[i] = weed_plant_copy(out_channels[i]);
1422       } else {
1423         xchan = out_channels[i];
1424       }
1425       xheights = weed_get_int_array(out_channels[i], WEED_LEAF_HEIGHT, NULL);
1426       height = xheights[1];
1427       dheight = xheights[0];
1428       offset = dheight * j;
1429       if ((height - offset) < dheight) dheight = height - offset;
1430       xheights[0] = dheight;
1431       weed_set_int_value(xchan, WEED_LEAF_OFFSET, offset);
1432       weed_set_int_array(xchan, WEED_LEAF_HEIGHT, 2, xheights);
1433       lives_free(xheights);
1434     }
1435 
1436     if (j < to_use - 1) {
1437       weed_set_plantptr_array(xinst[j], WEED_LEAF_OUT_CHANNELS, nchannels, xchannels);
1438     }
1439     lives_freep((void **)&xchannels);
1440 
1441     procvals[j].procfunc = process_func;
1442     procvals[j].inst = xinst[j];
1443     procvals[j].tc = tc; // use same timecode for all slices
1444 
1445     if (j < to_use - 1) {
1446       // start a thread for processing
1447       lives_thread_create(&dthreads[j], LIVES_THRDATTR_NONE, thread_process_func, &procvals[j]);
1448       nthreads++; // actual number of threads used
1449     } else {
1450       /// do the last portion oiurselves, rather than just waiting around
1451       (*thread_process_func)(&procvals[j]);
1452       retval = procvals[j].ret;
1453       if (retval == WEED_ERROR_PLUGIN_INVALID) plugin_invalid = TRUE;
1454       if (retval == WEED_ERROR_FILTER_INVALID) filter_invalid = TRUE;
1455       if (retval == WEED_ERROR_REINIT_NEEDED) needs_reinit = TRUE;
1456     }
1457   }
1458 
1459   // wait for threads to finish
1460   for (j = 0; j < nthreads; j++) {
1461     lives_thread_join(dthreads[j], NULL);
1462     retval = procvals[j].ret;
1463     if (retval == WEED_ERROR_PLUGIN_INVALID) plugin_invalid = TRUE;
1464     if (retval == WEED_ERROR_FILTER_INVALID) filter_invalid = TRUE;
1465     if (retval == WEED_ERROR_REINIT_NEEDED) needs_reinit = TRUE;
1466 
1467     xchannels = weed_get_plantptr_array(xinst[j], WEED_LEAF_OUT_CHANNELS, NULL);
1468     for (i = 0; i < nchannels; i++) {
1469       weed_plant_free(xchannels[i]);
1470     }
1471     lives_freep((void **)&xchannels);
1472     weed_plant_free(xinst[j]);
1473   }
1474 
1475   for (i = 0; i < nchannels; i++) {
1476     // reset the channel heights
1477     xheights = weed_get_int_array(out_channels[i], WEED_LEAF_HEIGHT, NULL);
1478     weed_set_int_value(out_channels[i], WEED_LEAF_HEIGHT, xheights[1]);
1479     lives_free(xheights);
1480     weed_leaf_delete(out_channels[i], WEED_LEAF_OFFSET);
1481   }
1482 
1483   lives_freep((void **)&procvals);
1484   lives_freep((void **)&xinst);
1485   lives_freep((void **)&dthreads);
1486   lives_freep((void **)&out_channels);
1487   weed_leaf_delete(inst, WEED_LEAF_HOST_UNUSED);
1488 
1489   if (plugin_invalid) return FILTER_ERROR_INVALID_PLUGIN;
1490   if (filter_invalid) return FILTER_ERROR_INVALID_FILTER;
1491   if (needs_reinit) return FILTER_ERROR_NEEDS_REINIT; // TODO...
1492   return FILTER_SUCCESS;
1493 }
1494 
1495 
check_cconx(weed_plant_t * inst,int nchans,boolean * needs_reinit)1496 static lives_filter_error_t check_cconx(weed_plant_t *inst, int nchans, boolean *needs_reinit) {
1497   weed_plant_t **in_channels;
1498 
1499   // we stored original key/mode to use here
1500   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) {
1501     // pull from alpha chain
1502     int key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL), mode;
1503     if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_MODE)) {
1504       mode = weed_get_int_value(inst, WEED_LEAF_HOST_MODE, NULL);
1505     } else mode = key_modes[key];
1506 
1507     // need to do this AFTER setting in-channel size
1508     if (mainw->cconx) {
1509       // chain any alpha channels
1510       if (cconx_chain_data(key, mode)) *needs_reinit = TRUE;
1511     }
1512   }
1513 
1514   // make sure we have pixel_data for all mandatory in alpha channels (from alpha chains)
1515   // if not, if the ctmpl is optnl mark as host_temp_disabled; else return with error
1516 
1517   in_channels = weed_get_plantptr_array(inst, WEED_LEAF_IN_CHANNELS, NULL);
1518 
1519   for (int i = 0; i < nchans; i++) {
1520     if (!weed_palette_is_alpha(weed_get_int_value(in_channels[i], WEED_LEAF_CURRENT_PALETTE, NULL))) continue;
1521 
1522     if (weed_plant_has_leaf(in_channels[i], WEED_LEAF_HOST_INTERNAL_CONNECTION)) {
1523       if (cconx_chain_data_internal(in_channels[i])) *needs_reinit = TRUE;
1524     }
1525 
1526     if (!weed_get_voidptr_value(in_channels[i], WEED_LEAF_PIXEL_DATA, NULL)) {
1527       weed_plant_t *chantmpl = weed_get_plantptr_value(in_channels[i], WEED_LEAF_TEMPLATE, NULL);
1528       if (weed_plant_has_leaf(chantmpl, WEED_LEAF_MAX_REPEATS) || (weed_chantmpl_is_optional(chantmpl)))
1529         if (weed_get_boolean_value(in_channels[i], WEED_LEAF_DISABLED, NULL) == WEED_FALSE)
1530           weed_set_boolean_value(in_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, WEED_TRUE);
1531         else weed_set_boolean_value(in_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, WEED_FALSE);
1532       // WEED_LEAF_DISABLED will serve instead
1533       else {
1534         lives_freep((void **)&in_channels);
1535         return FILTER_ERROR_MISSING_CHANNEL;
1536       }
1537     }
1538   }
1539 
1540   lives_freep((void **)&in_channels);
1541   return FILTER_SUCCESS;
1542 }
1543 
1544 
1545 /**
1546    @brief process a single video filter instance
1547 
1548    get in_tracks and out_tracks from the init_event; these map filter_instance channels to layers
1549 
1550    clear WEED_LEAF_DISABLED if we have non-zero frame and there is no WEED_LEAF_DISABLED in template
1551    if we have a zero (NULL) frame, set WEED_LEAF_DISABLED if WEED_LEAF_OPTIONAL, otherwise we cannot apply the filter
1552 
1553    set channel timecodes
1554 
1555    set the fps (data) in the instance
1556 
1557    pull pixel_data or wait for threads to complete for all input layers (unless it is there already)
1558 
1559    set each channel width, height to match largest (or smallest depending on quality level - TODO)
1560    of in layers (unless the plugin allows differing sizes)
1561 
1562    if width and height are wrong, resize in the layer
1563 
1564    if palette is wrong, first we try to change the plugin channel palette,
1565    if not possible we convert palette in the layer
1566 
1567    apply the effect, put result in output layer, set layer palette, width, height, rowstrides
1568 
1569    if filter does not support inplace, we must create a new pixel_data; this will then replace the original layer
1570    (this is passed to the plugin as the output, so we just copy by reference)
1571 
1572    for in/out alpha channels, there is no matching layer. These channels are passed around like data
1573    using mainw->cconx as a guide. We will simply free any in alpha channels after processing (unless inplace was used)
1574 
1575    WARNING: output layer may need resizing, and its palette may need adjusting - should be checked by the caller
1576 
1577    opwidth and opheight limit the maximum frame size, they can either be set to 0,0 or to max display size;
1578    however if all sources are smaller than this
1579    then the output will be smaller also and need resizing by the caller
1580 
1581    for purely audio filters, these are handled in weed_apply_audio_instance()
1582 
1583    for filters with mixed video / audio inputs (currently only generators) audio is added
1584 */
weed_apply_instance(weed_plant_t * inst,weed_plant_t * init_event,weed_plant_t ** layers,int opwidth,int opheight,weed_timecode_t tc)1585 lives_filter_error_t weed_apply_instance(weed_plant_t *inst, weed_plant_t *init_event, weed_plant_t **layers,
1586     int opwidth, int opheight, weed_timecode_t tc) {
1587   // filter_mutex should be unlocked
1588 
1589   void *pdata;
1590 
1591   weed_plant_t **in_channels = NULL, **out_channels = NULL, *channel, *chantmpl;
1592   weed_plant_t **in_ctmpls;
1593   weed_plant_t *def_channel = NULL;
1594   weed_plant_t *filter = weed_instance_get_filter(inst, FALSE);
1595   weed_layer_t *layer = NULL, *orig_layer = NULL;
1596 
1597   int *in_tracks, *out_tracks;
1598   int *rowstrides;
1599   int *palettes;
1600   int *channel_rows;
1601   int *mand;
1602   lives_filter_error_t retval = FILTER_SUCCESS;
1603 
1604   short pb_quality = prefs->pb_quality;
1605 
1606   boolean rowstrides_changed;
1607   boolean needs_reinit = FALSE, inplace = FALSE;
1608   boolean all_out_alpha = TRUE; //,all_in_alpha=FALSE;
1609   boolean is_converter = FALSE, pvary = FALSE, svary = FALSE;
1610   boolean resized = FALSE, letterboxed = FALSE;
1611   boolean letterbox = FALSE;
1612 
1613   int num_palettes, num_in_tracks = 0, num_out_tracks;
1614   int inwidth, inheight, inpalette, outpalette, opalette, channel_flags, filter_flags = 0;
1615   int palette, cpalette, def_palette = 0;
1616   int outwidth, outheight;
1617   int numplanes = 0, width, height, xwidth, xheight;
1618   int nchr;
1619   int maxinwidth = 4, maxinheight = 4, mininwidth = -1, mininheight = -1;
1620   int iclamping, isampling, isubspace;
1621   int clip = -1;
1622   frames_t frame;
1623   int num_ctmpl, num_inc, num_outc;
1624   int osubspace = -1, osampling = -1, oclamping = -1;
1625   int num_in_alpha = 0, num_out_alpha = 0;
1626   int nmandout = 0;
1627   int lcount = 0;
1628   int key = -1;
1629   int i, j, k;
1630 
1631   if (prefs->dev_show_timing)
1632     g_printerr("apply inst @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
1633 
1634   if (weed_get_boolean_value(inst, WEED_LEAF_HOST_INITED, NULL) == WEED_FALSE) needs_reinit = TRUE;
1635 
1636   if (LIVES_IS_RENDERING) pb_quality = PB_QUALITY_HIGH;
1637 
1638   // TODO - check if inited, if not return with error (check also for filters with no init_func)
1639 
1640   /// pb_quality: HIGH == use MAX(in sizes, out size); MED == use MIN( MAX in sizes, output_size), LOW == use MIN(in sizes, out size
1641   if (pb_quality == PB_QUALITY_HIGH) {
1642     if (opwidth > 0)
1643       maxinwidth = opwidth;
1644     if (opheight > 0)
1645       maxinheight = opheight;
1646   }
1647 
1648   out_channels = weed_get_plantptr_array(inst, WEED_LEAF_OUT_CHANNELS, NULL);
1649 
1650   if (!out_channels && (mainw->preview || mainw->is_rendering) && num_compound_fx(inst) == 1) {
1651     /// skip filters with no output channels (we'll deal with them elsewhere)
1652     lives_freep((void **)&out_channels);
1653     return retval;
1654   }
1655 
1656   filter_flags = weed_get_int_value(filter, WEED_LEAF_FLAGS, NULL);
1657   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
1658 
1659   if (is_pure_audio(filter, TRUE)) {
1660     /// we process audio effects elsewhere
1661     lives_freep((void **)&out_channels);
1662     return FILTER_ERROR_IS_AUDIO;
1663   }
1664 
1665   in_channels = weed_get_plantptr_array(inst, WEED_LEAF_IN_CHANNELS, NULL);
1666 
1667   // here, in_tracks and out_tracks map our layers to in_channels and out_channels in the filter
1668   if (!in_channels || !has_video_chans_in(filter, TRUE) || all_ins_alpha(filter, TRUE)) {
1669     if ((!out_channels && weed_plant_has_leaf(inst, WEED_LEAF_OUT_PARAMETERS)) ||
1670         (all_outs_alpha(filter, TRUE))) {
1671       /// if alpha out(s) we need to construct the output frames
1672       for (i = 0; (channel = get_enabled_channel(inst, i, FALSE)) != NULL; i++) {
1673         pdata = weed_get_voidptr_value(channel, WEED_LEAF_PIXEL_DATA, NULL);
1674 
1675         if (!pdata) {
1676           width = DEF_FRAME_HSIZE_43S_UNSCALED; // TODO: default size for new alpha only channels
1677           height = DEF_FRAME_VSIZE_43S_UNSCALED; // TODO: default size for new alpha only channels
1678 
1679           weed_set_int_value(channel, WEED_LEAF_WIDTH, width);
1680           weed_set_int_value(channel, WEED_LEAF_HEIGHT, height);
1681 
1682           set_channel_size(filter, channel, width, height);
1683 
1684           if (weed_plant_has_leaf(filter, WEED_LEAF_ALIGNMENT_HINT)) {
1685             int rowstride_alignment_hint = weed_get_int_value(filter, WEED_LEAF_ALIGNMENT_HINT, NULL);
1686             if (rowstride_alignment_hint  > ALIGN_DEF)
1687               THREADVAR(rowstride_alignment_hint) = rowstride_alignment_hint;
1688           }
1689 
1690           // this will look at width, height, current_palette, and create an empty pixel_data and set rowstrides
1691           // and update width and height if necessary
1692           if (!create_empty_pixel_data(channel, FALSE, TRUE)) {
1693             retval = FILTER_ERROR_MEMORY_ERROR;
1694             goto done_video;
1695           }
1696         }
1697       }
1698 
1699       // TODO - this is more complex, as we have to check the entire chain of fx
1700 
1701       /// run it only if it outputs into effects which have video chans
1702       /// if (!feeds_to_video_filters(key,rte_key_getmode(key+1))) return FILTER_ERROR_NO_IN_CHANNELS;
1703       retval = run_process_func(inst, tc, key);
1704 
1705       lives_freep((void **)&in_channels);
1706       return retval;
1707     }
1708     lives_freep((void **)&in_channels);
1709     lives_freep((void **)&out_channels);
1710     return FILTER_ERROR_NO_IN_CHANNELS;
1711   }
1712 
1713   if (!get_enabled_channel(inst, 0, TRUE)) {
1714     /// we process generators elsewhere
1715     lives_freep((void **)&in_channels);
1716     lives_freep((void **)&out_channels);
1717     return FILTER_ERROR_NO_IN_CHANNELS;
1718   }
1719 
1720   if (!init_event) {
1721     num_in_tracks = enabled_in_channels(inst, FALSE);
1722     in_tracks = (int *)lives_calloc(2, sizint);
1723     in_tracks[1] = 1;
1724     num_out_tracks = enabled_out_channels(inst, FALSE);
1725     out_tracks = (int *)lives_calloc(1, sizint);
1726   } else {
1727     in_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_IN_TRACKS, &num_in_tracks);
1728     out_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_OUT_TRACKS, &num_out_tracks);
1729   }
1730 
1731   /// handle case where in_tracks[i] > than num layers
1732   /// either we temporarily disable the channel, or we can't apply the filter
1733   num_inc = weed_leaf_num_elements(inst, WEED_LEAF_IN_CHANNELS);
1734 
1735   for (i = 0; i < num_inc; i++) {
1736     if (weed_palette_is_alpha(weed_get_int_value(in_channels[i], WEED_LEAF_CURRENT_PALETTE, NULL)) &&
1737         weed_get_boolean_value(in_channels[i], WEED_LEAF_DISABLED, NULL) == WEED_FALSE)
1738       num_in_alpha++;
1739   }
1740 
1741   num_inc -= num_in_alpha;
1742 
1743   retval = check_cconx(inst, num_inc + num_in_alpha, &needs_reinit);
1744   if (retval != FILTER_SUCCESS) {
1745     goto done_video;
1746   }
1747 
1748   if (num_in_tracks > num_inc) num_in_tracks = num_inc; // for example, compound fx
1749 
1750   /// if we have more in_channels in the effect than in_tracks, we MUST (temp) disable the extra in_channels
1751   if (num_inc > num_in_tracks) {
1752     for (i = num_in_tracks; i < num_inc + num_in_alpha; i++) {
1753       if (!weed_palette_is_alpha(weed_channel_get_palette(in_channels[i]))) {
1754         if (weed_get_boolean_value(in_channels[i], WEED_LEAF_DISABLED, NULL) == WEED_FALSE)
1755           weed_set_boolean_value(in_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, WEED_TRUE);
1756         else weed_set_boolean_value(in_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, WEED_FALSE);
1757       }
1758     }
1759   }
1760 
1761   // count the actual layers fed in
1762   while (layers[lcount]) lcount++;
1763 
1764   for (k = i = 0; i < num_in_tracks; i++) {
1765     if (in_tracks[i] < 0) {
1766       retval = FILTER_ERROR_INVALID_TRACK; // probably audio
1767       goto done_video;
1768     }
1769 
1770     while (weed_palette_is_alpha(weed_channel_get_palette(in_channels[k]))) k++;
1771 
1772     channel = in_channels[k];
1773     weed_set_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, WEED_FALSE);
1774 
1775     if (in_tracks[i] >= lcount) {
1776       /// here we have more in_tracks than actual layers (this can happen if we have blank frames)
1777       /// disable some optional channels if we can
1778       for (j = k; j < num_in_tracks + num_in_alpha; j++) {
1779         if (weed_palette_is_alpha(weed_channel_get_palette(in_channels[j]))) continue;
1780         channel = in_channels[j];
1781         chantmpl = weed_get_plantptr_value(channel, WEED_LEAF_TEMPLATE, NULL);
1782         if (weed_plant_has_leaf(chantmpl, WEED_LEAF_MAX_REPEATS) || (weed_chantmpl_is_optional(chantmpl)))
1783           if (weed_get_boolean_value(channel, WEED_LEAF_DISABLED, NULL) == WEED_FALSE)
1784             weed_set_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, WEED_TRUE);
1785           else weed_set_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, WEED_FALSE);
1786         else {
1787           retval = FILTER_ERROR_MISSING_LAYER;
1788           goto done_video;
1789         }
1790       }
1791       break;
1792     }
1793     layer = layers[in_tracks[i]];
1794 
1795     if (!weed_get_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, NULL)) {
1796       /// wait for thread to pull layer pixel_data
1797       if (prefs->dev_show_timing)
1798         g_printerr("fx clr pre @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
1799       check_layer_ready(layer);
1800       if (prefs->dev_show_timing)
1801         g_printerr("fx clr post @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
1802       frame = weed_get_int_value(layer, WEED_LEAF_FRAME, NULL);
1803       if (frame == 0) {
1804         /// temp disable channels if we can
1805         channel = in_channels[k];
1806         chantmpl = weed_get_plantptr_value(channel, WEED_LEAF_TEMPLATE, NULL);
1807         if (weed_plant_has_leaf(chantmpl, WEED_LEAF_MAX_REPEATS) || (weed_chantmpl_is_optional(chantmpl))) {
1808           if (weed_get_boolean_value(channel, WEED_LEAF_DISABLED, NULL) == WEED_FALSE)
1809             weed_set_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, WEED_TRUE);
1810         } else {
1811           weed_set_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, WEED_FALSE);
1812           retval = FILTER_ERROR_BLANK_FRAME;
1813           goto done_video;
1814 	  // *INDENT-OFF*
1815 	}}}
1816     k++;
1817   }
1818   // *INDENT-ON*
1819 
1820   /// ensure all chantmpls NOT marked "optional" have at least one corresponding enabled channel
1821   /// e.g. we could have disabled all channels from a template with "max_repeats" that is not optional
1822   in_ctmpls = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, &num_ctmpl);
1823   mand = (int *)lives_calloc(num_ctmpl, sizint);
1824 
1825   for (i = 0; i < num_inc + num_in_alpha; i++) {
1826     /// skip disabled in channels
1827     if (weed_get_boolean_value(in_channels[i], WEED_LEAF_DISABLED, NULL) == WEED_TRUE ||
1828         weed_get_boolean_value(in_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) continue;
1829     chantmpl = weed_channel_get_template(in_channels[i]);
1830     for (j = 0; j < num_ctmpl; j++) {
1831       /// mark the non disabled in channels
1832       if (chantmpl == in_ctmpls[j]) {
1833         mand[j] = 1;
1834         break;
1835       }
1836     }
1837   }
1838 
1839   for (j = 0; j < num_ctmpl; j++) {
1840     /// quit if a mandatory channel has been disabled
1841     if (mand[j] == 0 && !weed_chantmpl_is_optional(in_ctmpls[j])) {
1842       lives_freep((void **)&in_ctmpls);
1843       lives_freep((void **)&mand);
1844       retval = FILTER_ERROR_MISSING_LAYER;
1845       goto done_video;
1846     }
1847   }
1848 
1849   lives_freep((void **)&in_ctmpls);
1850   lives_freep((void **)&mand);
1851 
1852   /// that is it for in_channels, now we move on to out_channels
1853 
1854   num_outc = weed_leaf_num_elements(inst, WEED_LEAF_OUT_CHANNELS);
1855 
1856   for (i = 0; i < num_outc; i++) {
1857     if (weed_palette_is_alpha(weed_channel_get_palette(out_channels[i]))) {
1858       if (weed_get_boolean_value(out_channels[i], WEED_LEAF_DISABLED, NULL) == WEED_FALSE)
1859         num_out_alpha++;
1860     } else {
1861       if (weed_get_boolean_value(out_channels[i], WEED_LEAF_DISABLED, NULL) == WEED_FALSE &&
1862           weed_get_boolean_value(out_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_FALSE) {
1863         nmandout++;
1864       }
1865     }
1866   }
1867 
1868   if (!init_event || num_compound_fx(inst) > 1) num_out_tracks -= num_out_alpha;
1869 
1870   if (num_out_tracks < 0) num_out_tracks = 0;
1871 
1872   if (nmandout > num_out_tracks) {
1873     /// occasionally during recording we get an init_event with no WEED_LEAF_OUT_TRACKS
1874     /// (probably when an audio effect inits/deinits a video effect)
1875     // - needs more investigation if it is still happening
1876     retval = FILTER_ERROR_MISSING_CHANNEL;
1877   }
1878 
1879   // pull frames for tracks
1880 
1881   for (i = 0; i < num_out_tracks + num_out_alpha; i++) {
1882     if (i >= num_outc) continue; // for compound filters, num_out_tracks may not be valid
1883     channel = out_channels[i];
1884     palette = weed_channel_get_palette(channel);
1885     if (weed_palette_is_alpha(palette)) continue;
1886     if (weed_get_boolean_value(channel, WEED_LEAF_DISABLED, NULL) == WEED_TRUE ||
1887         weed_get_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) continue;
1888     all_out_alpha = FALSE;
1889   }
1890 
1891   for (j = i = 0; i < num_in_tracks; i++) {
1892     if (weed_palette_is_alpha(weed_channel_get_palette(in_channels[j]))) continue;
1893     if (weed_get_boolean_value(in_channels[j], WEED_LEAF_DISABLED, NULL) == WEED_TRUE ||
1894         weed_get_boolean_value(in_channels[j], WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) {
1895       j++;
1896       continue;
1897     }
1898     layer = layers[in_tracks[i]];
1899     clip = weed_get_int_value(layer, WEED_LEAF_CLIP, NULL);
1900 
1901     if (!weed_layer_get_pixel_data_packed(layer)) {
1902       //check_layer_ready(layer);
1903       /* /// wait for thread to pull layer pixel_data */
1904       if (!is_layer_ready(layer)) {
1905 #define FX_WAIT_LIM 10000 // microseconds * 10
1906         if (prefs->dev_show_timing)
1907           g_printerr("fx clr2 pre @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
1908         for (register int tt = 0; tt < FX_WAIT_LIM && !is_layer_ready(layer); tt++) {
1909           lives_nanosleep(10000);
1910         }
1911         if (prefs->dev_show_timing)
1912           g_printerr("fx clr2 post @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
1913         if (!is_layer_ready(layer)) {
1914           retval = FILTER_ERROR_MISSING_FRAME;
1915           goto done_video;
1916         }
1917         if (!weed_layer_get_pixel_data_packed(layer)) {
1918           retval = FILTER_ERROR_MISSING_FRAME;
1919           goto done_video;
1920         }
1921       }
1922     }
1923     // we apply only transitions and compositors to the scrap file
1924     if (clip == mainw->scrap_file && num_in_tracks <= 1 && num_out_tracks <= 1) {
1925       retval = FILTER_ERROR_IS_SCRAP_FILE;
1926       goto done_video;
1927     }
1928     // use comparative widths - in RGB(A) pixels
1929     palette = weed_layer_get_palette(layer);
1930     inwidth = weed_layer_get_width_pixels(layer);
1931     inheight = weed_layer_get_height(layer);
1932 
1933     if (filter_flags & WEED_FILTER_CHANNEL_SIZES_MAY_VARY) svary = TRUE;
1934     if (filter_flags & WEED_FILTER_IS_CONVERTER) is_converter = TRUE;
1935 
1936     if (maxinwidth == 4 && inwidth > 4) maxinwidth = inwidth;
1937     if (maxinheight == 4 && inheight > 4) maxinheight = inheight;
1938 
1939     if ((mainw->multitrack && prefs->letterbox_mt) || (prefs->letterbox && !mainw->multitrack))
1940       letterbox = TRUE;
1941 
1942     if (!svary) {
1943       if (letterbox) {
1944         /// manual adjustment for letterboxing. We want to avoid the situation where some layers are letterboxing in
1945         /// one dimension and others are letterboxing in the other, however this is unavoidable with frames of different sizes.
1946         /// the best we can do is to make sure that one layer sets the collective size and the others will get letterboxed to that size.
1947         /// thus -
1948         /// if this layer is the first, or it engulfs all of the previous layers, then we let it set the width and height
1949         // otherwise, we will aspect ratio it to fit the max size. Since we know at least one of the dimensions was
1950         // within the current bounds (otherwise it would engulf), we will end up with 2 edges touching and two letterboxed
1951         if (inwidth >= maxinwidth && inheight >= maxinheight) {
1952           maxinwidth = inwidth;
1953           maxinheight = inheight;
1954         }
1955       } else {
1956         if (inwidth > maxinwidth) maxinwidth = inwidth;
1957         if (inheight > maxinheight) maxinheight = inheight;
1958       }
1959       if (prefs->pb_quality == PB_QUALITY_LOW) {
1960         // for low quality we pick the smallest dimensions
1961         if (mininwidth == -1 || inwidth < mininwidth) mininwidth = inwidth;
1962         if (mininheight == -1 || inheight < mininheight) mininheight = inheight;
1963       }
1964     }
1965     j++;
1966   }
1967 
1968   if (!svary || prefs->pb_quality == PB_QUALITY_LOW || !is_converter) {
1969     switch (pb_quality) {
1970     case PB_QUALITY_HIGH:
1971       if (maxinwidth > opwidth) opwidth = maxinwidth;
1972       if (maxinheight > opheight) opheight = maxinheight;
1973       break;
1974     case PB_QUALITY_MED:
1975       if (!mainw->multitrack) {
1976         if (maxinwidth > opwidth || maxinheight > opheight) {
1977           calc_maxspect(opwidth, opheight, &maxinwidth, &maxinheight);
1978           opwidth = maxinwidth;
1979           opheight = maxinheight;
1980         } else {
1981           calc_maxspect(maxinwidth, maxinheight, &opwidth, &opheight);
1982         }
1983       } else {
1984         if (maxinwidth > opwidth) maxinwidth = opwidth;
1985         if (maxinheight > opheight) maxinheight = opheight;
1986         opwidth = maxinwidth;
1987         opheight = maxinheight;
1988       }
1989       break;
1990     default:
1991       // PB_QUALITY_LOW
1992       if (!mainw->multitrack) {
1993         if (mininwidth < opwidth || mininheight < opheight) {
1994           calc_maxspect(mininwidth, mininheight, &opwidth, &opheight);
1995         } else {
1996           calc_maxspect(opwidth, opheight, &mininwidth, &mininheight);
1997           opwidth = mininwidth;
1998           opheight = mininheight;
1999         }
2000       } else {
2001         if (mininwidth < opwidth) opwidth = mininwidth;
2002         if (mininheight < opheight) opheight = mininheight;
2003       }
2004       break;
2005     }
2006   }
2007 
2008   opwidth = (opwidth >> 1) << 1;
2009   opheight = (opheight >> 1) << 1;
2010 
2011   if (pb_quality < PB_QUALITY_HIGH && mainw->multitrack && prefs->letterbox_mt) {
2012     calc_midspect(cfile->hsize, cfile->vsize, &opwidth, &opheight);
2013   }
2014 
2015   /// pass 1, we try to set channel sizes to opwidth X opheight
2016   /// channel may have restrictions (e.g fixed size or step values) so we will set it as near as we can
2017   /// we won't resize next as we will try to palette convert and resize in one go
2018   for (k = i = 0; k < num_inc + num_in_alpha; k++) {
2019     int owidth, oheight;
2020 
2021     channel = get_enabled_channel(inst, k, TRUE);
2022     if (!channel) break;
2023 
2024     if (weed_get_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) continue;
2025 
2026     if (!def_channel) def_channel = channel;
2027 
2028     if (weed_palette_is_alpha(weed_channel_get_palette(channel))) {
2029       /// currently, alpha channels don't come in layers, we add them like data directly to the channel
2030       /// plus, we ignore the display size (it doesn't make sense to resize an alpha channel)
2031       /// so if the filter allows varying channel sizes we are OK
2032       /// else if its not the first channel and the size differs and the filter doesnt allow varying sizes then we're in trouble !
2033       if (!svary && def_channel != channel && (weed_channel_get_width(def_channel) != weed_channel_get_width(channel)
2034           || weed_channel_get_width(def_channel) != weed_channel_get_width(channel))) {
2035         retval = FILTER_ERROR_UNABLE_TO_RESIZE;
2036         goto done_video;
2037       }
2038       continue;
2039     }
2040 
2041     layer = layers[in_tracks[i]];
2042 
2043     // values in pixels
2044     width = opwidth;
2045     height = opheight;
2046 
2047     if (svary) {
2048       /// if sizes can vary then we must set the first in channel to op size, because we'll want the first out channel at that size
2049       /// otherwise (or if it's a converter) then we can use layer size
2050       if (is_converter || def_channel != channel) {
2051         palette = weed_layer_get_palette(layer);
2052         inwidth = weed_get_int_value(layer, WEED_LEAF_WIDTH, NULL);
2053         inheight = weed_get_int_value(layer, WEED_LEAF_HEIGHT, NULL);
2054         width = inwidth * weed_palette_get_pixels_per_macropixel(palette);
2055         height = inheight;
2056       }
2057     }
2058 
2059     cpalette = weed_channel_get_palette(channel);
2060     width /= weed_palette_get_pixels_per_macropixel(cpalette); // convert width to channel macropixels
2061 
2062     /// check if the plugin needs reinit
2063     owidth = weed_channel_get_width(channel);
2064     oheight = weed_channel_get_height(channel);
2065 
2066     chantmpl = weed_channel_get_template(channel);
2067     channel_flags = weed_chantmpl_get_flags(chantmpl);
2068 
2069     if (owidth != width || oheight != height) {
2070       set_channel_size(filter, channel, width, height);
2071       opwidth = weed_channel_get_width(channel);
2072       opheight = weed_channel_get_height(channel);
2073       if (channel_flags & WEED_CHANNEL_REINIT_ON_SIZE_CHANGE) {
2074         boolean oneeds_reinit = needs_reinit;
2075         needs_reinit = TRUE;
2076         if (channel_flags & WEED_CHANNEL_NEEDS_NATURAL_SIZE) {
2077           int *nsizes = weed_get_int_array(channel, WEED_LEAF_NATURAL_SIZE, NULL);
2078           if (nsizes) {
2079             int *lnsizes;
2080             if (prefs->dev_show_timing)
2081               g_printerr("nsw pre2 @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2082             lives_nanosleep_until_nonzero(weed_plant_has_leaf(layer, WEED_LEAF_NATURAL_SIZE));
2083             if (prefs->dev_show_timing)
2084               g_printerr("nsw post2 @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2085             lnsizes = weed_get_int_array(layer, WEED_LEAF_NATURAL_SIZE, NULL);
2086             if (nsizes[0] == lnsizes[0] && nsizes[1] == lnsizes[1]) needs_reinit = oneeds_reinit;
2087             lives_free(lnsizes);
2088             lives_free(nsizes);
2089           }
2090         }
2091       }
2092     }
2093     if (prefs->dev_show_timing)
2094       g_printerr("nsw pre @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2095     if (channel_flags & WEED_CHANNEL_NEEDS_NATURAL_SIZE) {
2096       lives_nanosleep_until_nonzero(weed_plant_has_leaf(layer, WEED_LEAF_NATURAL_SIZE));
2097       weed_leaf_dup(channel, layer, WEED_LEAF_NATURAL_SIZE);
2098     }
2099     if (prefs->dev_show_timing)
2100       g_printerr("nsw post @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2101     i++;
2102   }
2103 
2104   if (!def_channel) {
2105     retval = FILTER_ERROR_NO_IN_CHANNELS;
2106     goto done_video;
2107   }
2108 
2109   /** now we do a second pass, and we set up the palettes on the in channels
2110     if it's the first channel, we try to change the channel palette to the layer palette*
2111     if that is not possible then we set it to the nearest alternative
2112     if it's not the first channel, we must use the palette from the first chan,
2113     unless the filter has WEED_FILTER_PALETTES_MAY_VARY.
2114 
2115      - if the channel template flags have WEED_FILTER_REINIT_ON_PALETTE_CHANGE, then we want to minimise palette changes, else
2116     we may be reiniting the filter instance a lot. If the clip type is CLIP_TYPE_FILE then we actually have 2 alternatives:
2117     the decoder palette, which will be used for unchanged frames pulled from the clip, + RGB24 which will be the case for
2118     altered frames loaded from png / jpeg. We want to avoid the worst case where we are reiniting on each alternate frame.
2119     so we'll pick in order of preference: RGB24, BGR24 (if the layer has no alpha), then RGBA32, BGRA32, ARGB32 (if it has alpha)
2120     (if the layer is already RGB we'll try that first). If no RGB palettes are available then we'll pick:
2121     YUV444P / YUVA4444P, YUV888 / YUVA8888, 422, 420 / UYVY / YUYV, 411
2122 
2123     There are two points where we can alter the palette: when the instance has just been inited (we can reinit with near zero cost)
2124     and if we are going to reinit anyway due to some other factor (e.g size change).
2125 
2126     IF the template has WEED_FILTER_REINIT_ON_ROWSTRIDES_CHANGE then similar rules apply excpet we will look at the macropixel
2127     sizes.
2128 
2129     Once the channel palette is set then we'll do a palette conversion / resize of the layer.
2130 
2131     TODO: we should be smarter here and figure out the best palette when forced to change,
2132     given the input palettes and output palettes
2133     of the entire filter chain from sources to output, since each change has a cost in terms of time and possibly quality.
2134 
2135     once the channel palette is set, then we may need to change the size and palette of the layer feeding in to it
2136     we do the resize first, since it may be possible to resize and palette convert all in one go
2137     after resizing we convert the palette if necessary
2138   */
2139   for (i = 0; i < num_in_tracks; i++) {
2140     int tgamma = WEED_GAMMA_UNKNOWN;
2141     if (i > 0) def_palette = weed_channel_get_palette(def_channel);
2142 
2143     channel = get_enabled_channel(inst, i, TRUE);
2144     if (channel) {
2145       chantmpl = weed_channel_get_template(channel);
2146       channel_flags = weed_chantmpl_get_flags(chantmpl);
2147     }
2148 
2149     //channel = get_enabled_channel(inst, i, TRUE);
2150     if (weed_get_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) continue;
2151 
2152     inpalette = weed_channel_get_palette(channel);
2153     if (weed_palette_is_alpha(inpalette)) continue;
2154 
2155     if (prefs->dev_show_timing)
2156       g_printerr("clrfx pre @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2157     layer = layers[in_tracks[i]];
2158     check_layer_ready(layer);
2159     if (prefs->dev_show_timing)
2160       g_printerr("clrfx post @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2161 
2162     cpalette = opalette = weed_layer_get_palette(layer);
2163     if (weed_palette_is_alpha(opalette)) continue;
2164 
2165     chantmpl = weed_channel_get_template(channel);
2166     channel_flags |= weed_chantmpl_get_flags(chantmpl);
2167 
2168     if (filter_flags & WEED_FILTER_PALETTES_MAY_VARY) {
2169       pvary = TRUE;
2170     } else if (i > 0) opalette = weed_channel_get_palette(def_channel);
2171 
2172     if ((channel_flags & WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE)
2173         || ((channel_flags & WEED_CHANNEL_REINIT_ON_ROWSTRIDES_CHANGE)
2174             && weed_palette_get_bits_per_macropixel(inpalette) != weed_palette_get_bits_per_macropixel(opalette))) {
2175       if (!needs_reinit && weed_get_boolean_value(inst, WEED_LEAF_HOST_UNUSED, NULL) == WEED_FALSE)
2176         /// use channel palette to avoid a reinit
2177         opalette = inpalette;
2178       else {
2179         int clip = weed_get_int_value(layer, WEED_LEAF_CLIP, NULL);
2180         if (clip == 0 || IS_NORMAL_CLIP(clip)) {
2181           if (!weed_palette_is_rgb(opalette))
2182             opalette = (weed_palette_has_alpha(opalette) ? WEED_PALETTE_RGBA32 : WEED_PALETTE_RGB24);
2183           needs_reinit = TRUE;
2184 	  // *INDENT-OFF*
2185 	}}}
2186     // *INDENT-ON*
2187 
2188     if (opalette != inpalette) {
2189       /// check which of the plugin's allowed palettes is closest to our target palette
2190       palettes = weed_chantmpl_get_palette_list(filter, chantmpl, &num_palettes);
2191       palette = best_palette_match(palettes, num_palettes, opalette);
2192       if (i > 0 && !pvary && palette != def_palette) {
2193         lives_freep((void **)&palettes);
2194         lives_freep((void **)&rowstrides);
2195         retval = FILTER_ERROR_TEMPLATE_MISMATCH; // plugin author messed up...
2196         goto done_video;
2197       }
2198 
2199       if (palette != inpalette) {
2200         if (channel_flags & WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE) needs_reinit = TRUE;
2201       }
2202       lives_freep((void **)&palettes);
2203       opalette = palette;
2204     }
2205 
2206     if (weed_palette_is_yuv(opalette)) {
2207       /// set up clamping, sampling and subspace for YUV palettes
2208       weed_channel_get_palette_yuv(channel, &iclamping, &isampling, &isubspace);
2209 
2210       if (!needs_reinit && (channel_flags & WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE) != 0
2211           && weed_get_boolean_value(inst, WEED_LEAF_HOST_UNUSED, NULL) == WEED_FALSE) {
2212         /// avoid a reinit if we can
2213         oclamping = iclamping;
2214         osampling = isampling;
2215         osubspace = isubspace;
2216       } else {
2217         if (i > 0 && !pvary) {
2218           weed_channel_get_palette_yuv(def_channel, &oclamping, &osampling, &osubspace);
2219         } else {
2220           // cpalette is layer palette here
2221           if ((i == 0 || pvary) && weed_palette_is_yuv(cpalette)) {
2222             oclamping = weed_get_int_value(layer, WEED_LEAF_YUV_CLAMPING, NULL);
2223           } else {
2224             if (i > 0)
2225               oclamping = weed_get_int_value(def_channel, WEED_LEAF_YUV_CLAMPING, NULL);
2226             else
2227               oclamping = WEED_YUV_CLAMPING_UNCLAMPED;
2228           }
2229           if (pvary && weed_plant_has_leaf(chantmpl, WEED_LEAF_YUV_CLAMPING)) {
2230             oclamping = weed_get_int_value(chantmpl, WEED_LEAF_YUV_CLAMPING, NULL);
2231           } else {
2232             if (weed_plant_has_leaf(filter, WEED_LEAF_YUV_CLAMPING)) {
2233               oclamping = weed_get_int_value(filter, WEED_LEAF_YUV_CLAMPING, NULL);
2234             }
2235           }
2236 
2237           // cpalette is layer palette here
2238           if (weed_palette_is_yuv(cpalette)) {
2239             // cant convert YUV <-> YUV sampling yet
2240             osampling = weed_get_int_value(layer, WEED_LEAF_YUV_SAMPLING, NULL);
2241           } else {
2242             if (i > 0)
2243               osampling = weed_get_int_value(def_channel, WEED_LEAF_YUV_SAMPLING, NULL);
2244             else
2245               osampling = WEED_YUV_SAMPLING_DEFAULT;
2246             if (pvary && weed_plant_has_leaf(chantmpl, WEED_LEAF_YUV_SAMPLING)) {
2247               osampling = weed_get_int_value(chantmpl, WEED_LEAF_YUV_SAMPLING, NULL);
2248             } else if (weed_plant_has_leaf(filter, WEED_LEAF_YUV_SAMPLING)) {
2249               osampling = weed_get_int_value(filter, WEED_LEAF_YUV_SAMPLING, NULL);
2250             }
2251           }
2252 
2253           // cpalette is layer palette here
2254           if (weed_palette_is_yuv(cpalette)) {
2255             // cant convert YUV <-> YUV subspace yet
2256             osubspace = weed_get_int_value(layer, WEED_LEAF_YUV_SUBSPACE, NULL);
2257           } else {
2258             if (i > 0)
2259               osubspace = weed_get_int_value(def_channel, WEED_LEAF_YUV_SUBSPACE, NULL);
2260             else
2261               osampling = WEED_YUV_SUBSPACE_YUV;
2262             if (pvary && weed_plant_has_leaf(chantmpl, WEED_LEAF_YUV_SUBSPACE)) {
2263               osubspace = weed_get_int_value(chantmpl, WEED_LEAF_YUV_SUBSPACE, NULL);
2264             } else {
2265               if (weed_plant_has_leaf(filter, WEED_LEAF_YUV_SUBSPACE)) {
2266                 osubspace = weed_get_int_value(filter, WEED_LEAF_YUV_SUBSPACE, NULL);
2267               } else
2268                 osubspace = WEED_YUV_SUBSPACE_YUV;
2269             }
2270           }
2271         }
2272 
2273         if (channel_flags & WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE) {
2274           if (oclamping != iclamping || isampling != osampling || isubspace != osubspace) {
2275             // we need to reinit due to yuv changes, see if we can switch to RGB
2276             if ((i == 0 || pvary) && IS_NORMAL_CLIP(clip)) {
2277               if (!weed_palette_is_rgb(opalette)) opalette = (weed_palette_has_alpha(opalette) ? WEED_PALETTE_RGBA32
2278                     : WEED_PALETTE_RGB24);
2279               if (opalette != palette) {
2280                 /// check which of the plugin's allowed palettes is closest to our target palette
2281                 palettes = weed_chantmpl_get_palette_list(filter, chantmpl, &num_palettes);
2282                 opalette = best_palette_match(palettes, num_palettes, opalette);
2283               }
2284             }
2285             needs_reinit = TRUE;
2286 	    // *INDENT-OFF*
2287           }}}}
2288     // *INDENT-ON*
2289 
2290     orig_layer = NULL;
2291 
2292     // cpalette is layer palette here, opalette is now channel palette
2293     if (cpalette != opalette || (weed_palette_is_yuv(opalette) && weed_palette_is_yuv(cpalette)
2294                                  && weed_get_int_value(layer, WEED_LEAF_YUV_CLAMPING, NULL) != oclamping)) {
2295       if (all_out_alpha && (weed_palette_is_lower_quality(opalette, cpalette) ||
2296                             (weed_palette_is_rgb(inpalette) &&
2297                              !weed_palette_is_rgb(cpalette)) ||
2298                             (weed_palette_is_rgb(cpalette) &&
2299                              !weed_palette_is_rgb(inpalette)))) {
2300         /// for an analyser (no out channels, or only alpha outs) we copy the layer if it needs lower quality
2301         /// since won't need to pass the output to anything else, we'll just destroy the copy after
2302         orig_layer = layer;
2303         layer = weed_layer_copy(NULL, orig_layer);
2304         weed_set_plantptr_value(orig_layer, WEED_LEAF_DUPLICATE, layer);
2305       }
2306     }
2307 
2308     /// we'll resize first, since we may be able to change the palette at the same time
2309 
2310     /// check if we need to resize the layer
2311     /// get the pixel widths to compare
2312     /// the channel has the sizes we set in pass1
2313 
2314     width = opwidth;
2315     height = opheight;
2316 
2317     xwidth = inwidth = weed_layer_get_width_pixels(layer);
2318     xheight = inheight = weed_layer_get_height(layer);
2319 
2320     resized = FALSE;
2321     letterboxed = FALSE;
2322 
2323     // we are comparing the macropixel sizes which is fine because that won't change
2324     // regardless of the channel / layer palette, but for resize_layer we need the pixel size,
2325     // which will be width * pixels_per_macropixel of the original channel palette, which is the divisor we used when
2326     // setting the channel width. resize_layer() will handle conversion of the macropixel sizes between layer palette
2327     // and opalette
2328     if ((!svary && (inwidth != width || inheight != height))
2329         || (svary && (inwidth > width || inheight > height || letterbox))) {
2330       short interp = get_interp_value(pb_quality, TRUE);
2331       if (letterbox && !orig_layer) {
2332         // if we are letterboxing, as well as letterboxing the final output in the player, we will also letterbox each layer into its channel
2333         // if we only have 1 layer this is irrelevant since the channel size == layer size, (or in high quality, channel size == player size,
2334         /// but the player size would have been adjusted to the letterboxed size in any case).
2335         /// However, when mixing channels, all will have
2336         // the same size, which was set by largest / smallest engulfing channel. [unless the filter set WEED_FILTER_SIZES_MAY_VARY]
2337         // Depending on the quality setting, this may be the largest or the smallest.
2338         // thus one layer / channel will fill the image, whilst all others may get letterboxed
2339         // another alternative would be to resize all of the layers, and ignore letterboxing for the intermediate stages,
2340         //// but for now we assume if the user wants letterboxing then it applies
2341         // to all layers
2342         int lbvals[4];
2343         calc_maxspect(width, height, &xwidth, &xheight);
2344 
2345         if (xwidth != width || height != xheight) {
2346           if (!letterbox_layer(layer, width, height, xwidth, xheight, interp, opalette, oclamping)) {
2347             retval = FILTER_ERROR_UNABLE_TO_RESIZE;
2348             goto done_video;
2349           }
2350           resized = TRUE;
2351           lbvals[0] = (width - xwidth) >> 1;
2352           lbvals[1] = (height - xheight) >> 1;
2353           lbvals[2] = xwidth;
2354           lbvals[3] = xheight;
2355           weed_set_int_array(channel, WEED_LEAF_INNER_SIZE, 4, lbvals);
2356           if (!mainw->multitrack && i > 0 && mainw->blend_palette == WEED_PALETTE_END) {
2357             mainw->blend_palette = weed_layer_get_palette_yuv(layer, &mainw->blend_clamping, &mainw->blend_sampling,
2358                                    &mainw->blend_subspace);
2359             mainw->blend_width = xwidth;
2360             mainw->blend_height = xheight;
2361             mainw->blend_gamma = weed_layer_get_gamma(layer);
2362           }
2363         }
2364       }
2365       if (!resized) {
2366         if (weed_plant_has_leaf(channel, WEED_LEAF_INNER_SIZE))
2367           weed_leaf_delete(channel, WEED_LEAF_INNER_SIZE);
2368         if (!resize_layer(layer, width, height, interp, opalette, oclamping)) {
2369           retval = FILTER_ERROR_UNABLE_TO_RESIZE;
2370           goto done_video;
2371         }
2372 
2373         if (!mainw->multitrack && i > 0 && mainw->blend_palette == WEED_PALETTE_END) {
2374           mainw->blend_palette = weed_layer_get_palette_yuv(layer, &mainw->blend_clamping, &mainw->blend_sampling,
2375                                  &mainw->blend_subspace);
2376           mainw->blend_width = width;
2377           mainw->blend_height = height;
2378           mainw->blend_gamma = weed_layer_get_gamma(layer);
2379         }
2380       }
2381     } else {
2382       if (weed_plant_has_leaf(channel, WEED_LEAF_INNER_SIZE))
2383         weed_leaf_delete(channel, WEED_LEAF_INNER_SIZE);
2384     }
2385 
2386     // check palette again in case it changed during resize
2387     cpalette = weed_layer_get_palette(layer);
2388 
2389     if (prefs->apply_gamma && weed_palette_is_rgb(opalette)) {
2390       // apply gamma conversion if plugin requested it
2391       if (filter_flags & WEED_FILTER_PREF_LINEAR_GAMMA)
2392         tgamma = WEED_GAMMA_LINEAR;
2393       else
2394         tgamma = cfile->gamma_type;
2395     }
2396 
2397     if (cpalette != opalette) {
2398       if (prefs->dev_show_timing)
2399         g_printerr("clpal1 pre @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2400       if (!convert_layer_palette_full(layer, opalette, oclamping, osampling, osubspace, tgamma)) {
2401         retval = FILTER_ERROR_INVALID_PALETTE_CONVERSION;
2402         goto done_video;
2403       }
2404       if (prefs->dev_show_timing)
2405         g_printerr("clpal1 post @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2406     }
2407 
2408     /// check if the plugin needs reinit
2409     // at this stage we still haven't updated values in the channel, except for width and height
2410     channel_rows = weed_channel_get_rowstrides(channel, &nchr);
2411 
2412     // check layer rowstrides against previous settings
2413     rowstrides = weed_layer_get_rowstrides(layer, &numplanes);
2414     rowstrides_changed = rowstrides_differ(numplanes, rowstrides, nchr, channel_rows);
2415     lives_free(channel_rows);
2416     lives_free(rowstrides);
2417 
2418     if (rowstrides_changed && (channel_flags & WEED_CHANNEL_REINIT_ON_ROWSTRIDES_CHANGE))
2419       needs_reinit = TRUE;
2420 
2421     if (tgamma != WEED_GAMMA_UNKNOWN) {
2422       if (prefs->dev_show_timing)
2423         g_printerr("gamma1 pre @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2424       if (letterboxed)
2425         gamma_convert_sub_layer(tgamma, 1.0, layer, (width - xwidth) / 2, (height - xheight) / 2,
2426                                 xwidth, xheight, TRUE);
2427       else
2428         gamma_convert_layer(tgamma, layer);
2429       if (prefs->dev_show_timing)
2430         g_printerr("gamma1 post @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2431     }
2432 
2433     /// since layers and channels are interchangeable, we just call weed_layer_copy(channel, layer)
2434     /// the cast to weed_layer_t * is unnecessary, but added for clarity
2435 
2436     /// weed_layer_copy() will adjust all the important leaves in the channel:
2437     // width, height, rowstrides, current palette, pixel_data,
2438     // YUV_*, etc.
2439     if (!weed_layer_copy((weed_layer_t *)channel, layer)) {
2440       retval = FILTER_ERROR_COPYING_FAILED;
2441       goto done_video;
2442     }
2443     if (!resized) {
2444       if (!mainw->multitrack && i > 0 && mainw->blend_palette == WEED_PALETTE_END) {
2445         mainw->blend_palette = weed_layer_get_palette_yuv(layer, &mainw->blend_clamping, &mainw->blend_sampling,
2446                                &mainw->blend_subspace);
2447         mainw->blend_width = weed_layer_get_width(layer) * weed_palette_get_pixels_per_macropixel(mainw->blend_palette);
2448         mainw->blend_height = weed_layer_get_height(layer);
2449         mainw->blend_gamma = weed_layer_get_gamma(layer);
2450       }
2451     }
2452   }
2453 
2454   /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2455 
2456   /// That's it for in channels, now we move on to out channels
2457   /// now we don't need to resize / convert palette because we simply create new pixel_data for the channel
2458   for (i = 0; i < num_out_tracks + num_out_alpha; i++) {
2459     /// for the time being we only have a single out channel, so it simplifes things greatly
2460 
2461     channel = get_enabled_channel(inst, i, FALSE);
2462     if (!channel) break; // compound fx
2463 
2464     inpalette = weed_channel_get_palette(channel);
2465 
2466     if (!weed_palette_is_alpha(inpalette) && out_tracks[i] < 0) {
2467       retval = FILTER_ERROR_INVALID_TRACK; // probably audio
2468       goto done_video;
2469     }
2470 
2471     chantmpl = weed_channel_get_template(channel);
2472     channel_flags = weed_chantmpl_get_flags(chantmpl);
2473 
2474     /// store values to see if they changed
2475     rowstrides = weed_channel_get_rowstrides(channel, &numplanes);
2476     outwidth = weed_channel_get_width(channel);
2477     outheight = weed_channel_get_height(channel);
2478     outpalette = weed_channel_get_palette_yuv(channel, &oclamping, &osampling, &osubspace);
2479 
2480     /// set the timecode for the channel (this is optional, but its good to do to timestamp the output)
2481     weed_set_int64_value(channel, WEED_LEAF_TIMECODE, tc);
2482 
2483     // check for INPLACE. According to the spec we can make outputs other than the first inplace, but we'll skip that for now
2484     if (i == 0 &&
2485         (((weed_palette_is_alpha(weed_channel_get_palette(channel)) &&
2486            weed_palette_is_alpha(weed_channel_get_palette(def_channel))))
2487          || (in_tracks && out_tracks && in_tracks[0] == out_tracks[0]))) {
2488       if (channel_flags & WEED_CHANNEL_CAN_DO_INPLACE) {
2489         if (!(weed_palette_is_alpha(outpalette) &&
2490               weed_get_boolean_value(channel, WEED_LEAF_HOST_ORIG_PDATA, NULL) == WEED_TRUE)) {
2491           /// INPLACE
2492           palettes = weed_chantmpl_get_palette_list(filter, chantmpl, &num_palettes);
2493           palette = weed_channel_get_palette(def_channel);
2494           if (best_palette_match(palettes, num_palettes, palette) == palette) {
2495             if (!weed_layer_copy(channel, def_channel)) {
2496               retval = FILTER_ERROR_COPYING_FAILED;
2497               goto done_video;
2498             }
2499             inplace = TRUE;
2500           } else {
2501             lives_freep((void **)&rowstrides);
2502             retval = FILTER_ERROR_TEMPLATE_MISMATCH; // plugin author messed up...
2503             goto done_video;
2504           }
2505           if (weed_palette_is_alpha(palette)) {
2506             // protect our in / out channel from being freed()
2507             weed_set_boolean_value(channel, WEED_LEAF_HOST_ORIG_PDATA, WEED_TRUE);
2508           }
2509           lives_freep((void **)&palettes);
2510           weed_set_boolean_value(channel, WEED_LEAF_HOST_INPLACE, WEED_TRUE);
2511         }
2512       }
2513     }
2514 
2515     if (!inplace) {
2516       /// try to match palettes with first enabled in channel
2517       /// According to the spec, if the plugin permits we can match out channel n with in channel n
2518       /// but we'll skip that for now
2519       palette = weed_channel_get_palette(def_channel);
2520       if (palette != outpalette) {
2521         // palette change needed; try to change channel palette
2522         palettes = weed_chantmpl_get_palette_list(filter, chantmpl, &num_palettes);
2523         if ((outpalette = best_palette_match(palettes, num_palettes, palette)) == palette) {
2524           weed_set_int_value(channel, WEED_LEAF_CURRENT_PALETTE, palette);
2525         } else {
2526           lives_freep((void **)&palettes);
2527           lives_freep((void **)&rowstrides);
2528           retval = FILTER_ERROR_TEMPLATE_MISMATCH;
2529           goto done_video;
2530         }
2531         lives_freep((void **)&palettes);
2532       }
2533 
2534       weed_leaf_copy_or_delete(channel, WEED_LEAF_YUV_CLAMPING, def_channel);
2535       weed_leaf_copy_or_delete(channel, WEED_LEAF_YUV_SAMPLING, def_channel);
2536       weed_leaf_copy_or_delete(channel, WEED_LEAF_YUV_SUBSPACE, def_channel);
2537 
2538       if (svary && is_converter) {
2539         /// resizing - use the value we set in channel template
2540         /// this allows us to, for example, resize the same in_channel to multiple out_channels at various sizes
2541         width = weed_get_int_value(chantmpl, WEED_LEAF_HOST_WIDTH, NULL);
2542         width /= weed_palette_get_pixels_per_macropixel(weed_channel_get_palette(channel));
2543         height = weed_get_int_value(chantmpl, WEED_LEAF_HOST_HEIGHT, NULL);
2544       } else {
2545         // NB. in future if we add more out channels, if (svary) this should be the ith in_channel.
2546         width = weed_channel_get_width(def_channel);
2547         height = weed_channel_get_height(def_channel);
2548       }
2549 
2550       set_channel_size(filter, channel, width, height);
2551 
2552       if (weed_plant_has_leaf(filter, WEED_LEAF_ALIGNMENT_HINT)) {
2553         int rowstride_alignment_hint = weed_get_int_value(filter, WEED_LEAF_ALIGNMENT_HINT, NULL);
2554         if (rowstride_alignment_hint > ALIGN_DEF) {
2555           THREADVAR(rowstride_alignment_hint) = rowstride_alignment_hint;
2556         }
2557       }
2558 
2559       // this will look at width, height, current_palette, and create an empty pixel_data and set rowstrides
2560       // and update width and height if necessary
2561       if (!create_empty_pixel_data(channel, FALSE, TRUE)) {
2562         retval = FILTER_ERROR_MEMORY_ERROR;
2563         goto done_video;
2564       }
2565       if (filter_flags & WEED_FILTER_PREF_LINEAR_GAMMA)
2566         weed_channel_set_gamma_type(channel, WEED_GAMMA_LINEAR);
2567       else {
2568         if (CURRENT_CLIP_IS_VALID)
2569           weed_channel_set_gamma_type(channel, cfile->gamma_type);
2570       }
2571 
2572       weed_set_boolean_value(channel, WEED_LEAF_HOST_INPLACE, WEED_FALSE);
2573       // end of (!inplace)
2574     }
2575 
2576     /// check whether the filter needs a reinit
2577 
2578     channel_rows = weed_channel_get_rowstrides(channel, &nchr);
2579     palette = weed_channel_get_palette_yuv(channel, &iclamping, &isampling, &isubspace);
2580 
2581     rowstrides_changed = rowstrides_differ(nchr, channel_rows, numplanes, rowstrides);
2582 
2583     lives_freep((void **)&channel_rows);
2584     lives_freep((void **)&rowstrides);
2585 
2586     width = weed_channel_get_width(channel);
2587     height = weed_channel_get_height(channel);
2588 
2589     if ((rowstrides_changed && (channel_flags & WEED_CHANNEL_REINIT_ON_ROWSTRIDES_CHANGE))) needs_reinit = TRUE;
2590 
2591     if ((outwidth != width || outheight != height) && (channel_flags & WEED_CHANNEL_REINIT_ON_SIZE_CHANGE))
2592       needs_reinit = TRUE;
2593 
2594     if (palette != inpalette || (weed_palette_is_yuv(palette)
2595                                  && (oclamping != iclamping || osampling != isampling || osubspace != isubspace)))
2596       if (channel_flags & WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE) needs_reinit = TRUE;
2597   }
2598 
2599   /// all channels now set up
2600 
2601   if (needs_reinit) {
2602     if ((retval = weed_reinit_effect(inst, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
2603       goto done_video;
2604     }
2605   }
2606 
2607   if (prefs->apply_gamma) {
2608     // do gamma correction of any RGB(A) parameters
2609     gamma_conv_params(((filter_flags & WEED_FILTER_PREF_LINEAR_GAMMA))
2610                       ? WEED_GAMMA_LINEAR : WEED_GAMMA_SRGB, inst, TRUE);
2611     gamma_conv_params(((filter_flags & WEED_FILTER_PREF_LINEAR_GAMMA))
2612                       ? WEED_GAMMA_LINEAR : WEED_GAMMA_SRGB, inst, FALSE);
2613   }
2614 
2615   if (CURRENT_CLIP_IS_VALID)
2616     weed_set_double_value(inst, WEED_LEAF_FPS, cfile->pb_fps);
2617 
2618   //...finally we are ready to apply the filter
2619 
2620   // TODO - better error handling
2621   //...finally we are ready to apply the filter
2622   retval = run_process_func(inst, tc, key);
2623 
2624   /// do gamma correction of any integer RGB(A) parameters
2625   /// convert in and out parameters to SRGB
2626   /// TODO: store / restore old vals
2627   gamma_conv_params(WEED_GAMMA_SRGB, inst, TRUE);
2628   gamma_conv_params(WEED_GAMMA_SRGB, inst, FALSE);
2629 
2630   if (retval == FILTER_ERROR_INVALID_PLUGIN) {
2631     goto done_video;
2632   }
2633 
2634   if (retval == WEED_ERROR_REINIT_NEEDED) {
2635     needs_reinit = TRUE;
2636   }
2637 
2638   for (k = 0; k < num_inc + num_in_alpha; k++) {
2639     channel = get_enabled_channel(inst, k, TRUE);
2640     if (weed_palette_is_alpha(weed_channel_get_palette(channel))) {
2641       // free pdata for all alpha in channels, unless orig pdata was passed from a prior fx
2642       layer = layers[in_tracks[k]];
2643       weed_layer_pixel_data_free(channel);
2644       if (!weed_channel_get_pixel_data(channel)) {
2645         weed_layer_t *dupe = weed_get_plantptr_value(layers[in_tracks[i]], WEED_LEAF_DUPLICATE, NULL);
2646         if (dupe) weed_layer_nullify_pixel_data(dupe);
2647         else weed_layer_nullify_pixel_data(layer);
2648       }
2649     }
2650   }
2651 
2652   // now we write our out channels back to layers, leaving the palettes and sizes unchanged
2653 
2654   for (i = k = 0; k < num_out_tracks + num_out_alpha; k++) {
2655     channel = get_enabled_channel(inst, k, FALSE);
2656     if (!channel) break; // compound fx
2657 
2658     layer = layers[out_tracks[i]];
2659     weed_set_boolean_value(layer, "letterboxed", letterbox);
2660 
2661     if (weed_get_boolean_value(channel, WEED_LEAF_HOST_INPLACE, NULL) == WEED_TRUE) continue;
2662 
2663     if (weed_palette_is_alpha(weed_channel_get_palette(channel))) {
2664       // out chan data for alpha is freed after all fx proc - in case we need for in chans
2665       continue;
2666     }
2667 
2668     // free any existing pixel_data, we will replace it with the channel data
2669     weed_layer_pixel_data_free(layer);
2670 
2671     if (!weed_layer_copy(layer, channel)) {
2672       retval = FILTER_ERROR_COPYING_FAILED;
2673       goto done_video;
2674     }
2675     i++;
2676   }
2677 
2678   if (needs_reinit) {
2679     retval = FILTER_ERROR_NEEDS_REINIT;
2680   }
2681 
2682   for (i = 0; i < num_inc + num_in_alpha; i++) {
2683     if (weed_get_boolean_value(in_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) {
2684       weed_set_boolean_value(in_channels[i], WEED_LEAF_DISABLED, WEED_FALSE);
2685       weed_set_boolean_value(in_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, WEED_FALSE);
2686     }
2687   }
2688 
2689 done_video:
2690 
2691   for (i = 0; i < num_in_tracks; i++) {
2692     weed_plant_t *dupe;
2693     layer = layers[in_tracks[i]];
2694     check_layer_ready(layer);
2695     dupe = weed_get_plantptr_value(layer, WEED_LEAF_DUPLICATE, NULL);
2696     if (dupe) {
2697       weed_layer_free(dupe);
2698       weed_leaf_delete(layer, WEED_LEAF_DUPLICATE);
2699     }
2700   }
2701 
2702   lives_freep((void **)&in_tracks);
2703   lives_freep((void **)&out_tracks);
2704   lives_freep((void **)&in_channels);
2705   lives_freep((void **)&out_channels);
2706 
2707   return retval;
2708 }
2709 
2710 
enable_disable_channels(weed_plant_t * inst,boolean is_in,int * tracks,int num_tracks,int nbtracks,weed_plant_t ** layers)2711 static lives_filter_error_t enable_disable_channels(weed_plant_t *inst, boolean is_in, int *tracks, int num_tracks,
2712     int nbtracks,
2713     weed_plant_t **layers) {
2714   // handle case where in_channels > than num layers
2715   // either we temporarily disable the channel, or we can't apply the filter
2716   weed_plant_t *filter = weed_instance_get_filter(inst, FALSE);
2717   weed_plant_t *channel, **channels, *chantmpl, **ctmpls = NULL, *layer;
2718   int maxcheck = num_tracks, i, j, num_ctmpls, num_channels;
2719   void **pixdata = NULL;
2720   boolean *mand;
2721 
2722   if (is_in)
2723     channels = weed_get_plantptr_array_counted(inst, WEED_LEAF_IN_CHANNELS, &num_channels);
2724   else
2725     channels = weed_get_plantptr_array_counted(inst, WEED_LEAF_OUT_CHANNELS, &num_channels);
2726 
2727   if (num_tracks > num_channels) maxcheck = num_tracks = num_channels;
2728   if (num_channels > num_tracks) maxcheck = num_channels;
2729 
2730   for (i = 0; i < maxcheck; i++) {
2731     channel = channels[i];
2732     weed_set_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, WEED_FALSE);
2733 
2734     // skip disabled channels for now
2735     if (weed_channel_is_disabled(channel)) continue;
2736     if (i < num_tracks) layer = layers[tracks[i] + nbtracks];
2737     else layer = NULL;
2738 
2739     if (!layer || ((weed_layer_is_audio(layer) && !weed_layer_get_audio_data(layer, NULL)) ||
2740                    (weed_layer_is_video(layer) && (pixdata = weed_layer_get_pixel_data(layer, NULL)) == NULL))) {
2741       // if the layer data is NULL and it maps to a repeating channel, then disable the channel temporarily
2742       chantmpl = weed_channel_get_template(channel);
2743       if (weed_chantmpl_get_max_repeats(chantmpl) != 1) {
2744         if (!weed_channel_is_disabled(channel)) {
2745           weed_set_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, WEED_TRUE);
2746         } else weed_set_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, WEED_FALSE);
2747       } else {
2748         lives_free(channels);
2749         lives_freep((void **)&pixdata);
2750         lives_freep((void **)&ctmpls);
2751         return FILTER_ERROR_MISSING_LAYER;
2752       }
2753     }
2754   }
2755 
2756   lives_freep((void **)&pixdata);
2757 
2758   // ensure all chantmpls not marked "optional" have at least one corresponding enabled channel
2759   // e.g. we could have disabled all channels from a template with "max_repeats" that is not also "optional"
2760   if (is_in) ctmpls = weed_filter_get_in_chantmpls(filter, &num_ctmpls);
2761   else ctmpls = weed_filter_get_out_chantmpls(filter, &num_ctmpls);
2762 
2763   if (num_ctmpls > 0) {
2764     // step through all the active channels and mark the template as fulfilled
2765     mand = (int *)lives_calloc(num_ctmpls, sizint);
2766     for (i = 0; i < num_channels; i++) {
2767       if (weed_channel_is_disabled(channels[i]) ||
2768           weed_get_boolean_value(channels[i], WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) continue;
2769       chantmpl = weed_channel_get_template(channels[i]);
2770       for (j = 0; j < num_ctmpls; j++) {
2771         if (chantmpl == ctmpls[j]) {
2772           mand[j] = TRUE;
2773           break;
2774         }
2775       }
2776     }
2777 
2778     for (j = 0; j < num_ctmpls; j++) if (mand[j] == FALSE && (!weed_chantmpl_is_optional(ctmpls[j]))) {
2779         lives_freep((void **)&ctmpls);
2780         lives_freep((void **)&mand);
2781         lives_free(channels);
2782         lives_freep((void **)&ctmpls);
2783         return FILTER_ERROR_MISSING_LAYER;
2784       }
2785     lives_freep((void **)&ctmpls);
2786     lives_freep((void **)&mand);
2787   } else {
2788     lives_free(channels);
2789     lives_freep((void **)&ctmpls);
2790     return FILTER_ERROR_TEMPLATE_MISMATCH;
2791   }
2792 
2793   lives_freep((void **)&ctmpls);
2794   lives_free(channels);
2795   return FILTER_SUCCESS;
2796 }
2797 
2798 
run_process_func(weed_plant_t * instance,weed_timecode_t tc,int key)2799 lives_filter_error_t run_process_func(weed_plant_t *instance, weed_timecode_t tc, int key) {
2800   weed_plant_t *filter = weed_instance_get_filter(instance, FALSE);
2801   weed_process_f process_func;
2802   lives_filter_error_t retval = FILTER_SUCCESS;
2803   boolean did_thread = FALSE;
2804   int filter_flags = weed_get_int_value(filter, WEED_LEAF_FLAGS, NULL);
2805 #ifdef PLMEMCHECK
2806   int npl;
2807   weed_plant_t *och = weed_instance_get_out_channels(instance, &npl);
2808   if (och)
2809     weed_mem_chkreg(wee_channel_get_pdt, rs, ht, npl);
2810 #endif
2811 
2812   // see if we can multithread
2813   if ((prefs->nfx_threads = future_prefs->nfx_threads) > 1 &&
2814       filter_flags & WEED_FILTER_HINT_MAY_THREAD) {
2815     weed_plant_t **out_channels = weed_instance_get_out_channels(instance, NULL);
2816     if (key == -1 || !filter_mutex_trylock(key)) {
2817       retval = process_func_threaded(instance, tc);
2818       if (key != -1) filter_mutex_unlock(key);
2819     } else retval = FILTER_ERROR_INVALID_PLUGIN;
2820     lives_free(out_channels);
2821     if (retval != FILTER_ERROR_DONT_THREAD) did_thread = TRUE;
2822   }
2823   if (!did_thread) {
2824     // normal single threaded version
2825     process_func = (weed_process_f)weed_get_funcptr_value(filter, WEED_LEAF_PROCESS_FUNC, NULL);
2826     if (process_func && (key == -1 || !filter_mutex_trylock(key))) {
2827       weed_error_t ret = (*process_func)(instance, tc);
2828       if (key != -1) filter_mutex_unlock(key);
2829       if (ret == WEED_ERROR_PLUGIN_INVALID) retval = FILTER_ERROR_INVALID_PLUGIN;
2830     } else retval = FILTER_ERROR_INVALID_PLUGIN;
2831     weed_leaf_delete(instance, WEED_LEAF_HOST_UNUSED);
2832   }
2833 #ifdef PLMEMCHECK
2834   weed_mem_chkreg(NULL, NULL, 0, 0);
2835 #endif
2836   return retval;
2837 }
2838 
2839 
2840 /// here, in_tracks and out_tracks map our layers array to in_channels and out_channels in the filter
2841 /// each layer may only map to zero or one in channel and zero or one out channel.
2842 
2843 /// in addition, each mandatory channel in/out must have a layer mapped to it. If an optional, repeatable  channel is unmatched
2844 /// we disable it temporarily. We don't disable channels permanently here since other functions will handle that more complex issue.
weed_apply_audio_instance_inner(weed_plant_t * inst,weed_plant_t * init_event,weed_plant_t ** layers,weed_timecode_t tc,int nbtracks)2845 static lives_filter_error_t weed_apply_audio_instance_inner(weed_plant_t *inst, weed_plant_t *init_event,
2846     weed_plant_t **layers, weed_timecode_t tc, int nbtracks) {
2847   // filter_mutex MUST be unlocked
2848 
2849   // TODO - handle the following:
2850   // input audio_channels are mono, but the plugin NEEDS stereo; we should duplicate the audio.
2851 
2852   // TODO - handle plugin return errors better, eg. NEEDS_REINIT
2853 
2854   int *in_tracks = NULL, *out_tracks = NULL;
2855 
2856   weed_layer_t *layer;
2857   weed_plant_t **in_channels = NULL, **out_channels = NULL, *channel, *chantmpl;
2858 
2859   float **adata;
2860 
2861   lives_filter_error_t retval = FILTER_SUCCESS;
2862 
2863   int channel_flags;
2864   int num_in_tracks, num_out_tracks;
2865 
2866   int num_inc;
2867   int key = -1;
2868 
2869   int nchans = 0;
2870   int nsamps = 0;
2871 
2872   register int i, j;
2873 
2874   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
2875 
2876   if (!get_enabled_channel(inst, 0, TRUE)) {
2877     // we process generators elsewhere
2878     return FILTER_ERROR_NO_IN_CHANNELS;
2879   }
2880 
2881   if (!init_event) {
2882     num_in_tracks = enabled_in_channels(inst, FALSE);
2883     in_tracks = (int *)lives_calloc(2, sizint);
2884     in_tracks[1] = 1;
2885     if (!get_enabled_channel(inst, 0, FALSE)) num_out_tracks = 0;
2886     else {
2887       num_out_tracks = 1;
2888       out_tracks = (int *)lives_calloc(1, sizint);
2889     }
2890   } else {
2891     in_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_IN_TRACKS, &num_in_tracks);
2892     out_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_OUT_TRACKS, &num_out_tracks);
2893   }
2894 
2895   in_channels = weed_instance_get_in_channels(inst, &num_inc);
2896   if (num_in_tracks > num_inc) num_in_tracks = num_inc;
2897 
2898   for (i = 0; i < num_in_tracks; i++) {
2899     layer = layers[in_tracks[i] + nbtracks];
2900     channel = get_enabled_channel(inst, i, TRUE);
2901 
2902     if (weed_get_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) continue;
2903 
2904     weed_set_int64_value(channel, WEED_LEAF_TIMECODE, tc);
2905     weed_leaf_dup(channel, layer, WEED_LEAF_AUDIO_DATA_LENGTH);
2906     weed_leaf_dup(channel, layer, WEED_LEAF_AUDIO_RATE);
2907     weed_leaf_dup(channel, layer, WEED_LEAF_AUDIO_CHANNELS);
2908 
2909     /* g_print("setting ad for channel %d from layer %d. val %p, eg %f \n", i, in_tracks[i] + nbtracks, weed_get_voidptr_value(layer, "audio_data", NULL), ((float *)(weed_get_voidptr_value(layer, "audio_data", NULL)))[0]); */
2910     weed_leaf_dup(channel, layer, WEED_LEAF_AUDIO_DATA);
2911     /* g_print("setting afterval %p, eg %f \n", weed_get_voidptr_value(channel, "audio_data", NULL), ((float *)(weed_get_voidptr_value(channel, "audio_data", NULL)))[0]); */
2912   }
2913 
2914   // set up our out channels
2915   for (i = 0; i < num_out_tracks; i++) {
2916     weed_plant_t *inchan;
2917 
2918     if (out_tracks[i] != in_tracks[i]) {
2919       retval = FILTER_ERROR_INVALID_TRACK; // we dont do swapping around of audio tracks
2920       goto done_audio;
2921     }
2922 
2923     channel = get_enabled_channel(inst, i, FALSE);
2924     inchan = get_enabled_channel(inst, i, TRUE);
2925     layer = layers[out_tracks[i] + nbtracks];
2926 
2927     weed_set_int64_value(channel, WEED_LEAF_TIMECODE, tc);
2928     chantmpl = weed_channel_get_template(channel);
2929     channel_flags = weed_chantmpl_get_flags(chantmpl);
2930 
2931     /// set up the audio data for each out channel. IF we can inplace then we use the audio_data from the corresponding in_channel
2932     /// otherwise we allocate it
2933     if ((channel_flags & WEED_CHANNEL_CAN_DO_INPLACE)
2934         && weed_channel_get_audio_rate(channel) == weed_channel_get_audio_rate(inchan)
2935         && weed_channel_get_naudchans(channel) == weed_channel_get_naudchans(inchan)) {
2936       weed_set_boolean_value(layer, WEED_LEAF_HOST_INPLACE, WEED_TRUE);
2937       weed_leaf_dup(channel, inchan, WEED_LEAF_AUDIO_DATA);
2938       /* g_print("setting dfff ad for channel %p from chan %p, eg %f \n", weed_get_voidptr_value(channel, "audio_data", NULL), weed_get_voidptr_value(inchan, "audio_data", NULL), ((float *)(weed_get_voidptr_value(layer, "audio_data", NULL)))[0]); */
2939     } else {
2940       nchans = weed_layer_get_naudchans(layer);
2941       adata = (float **)lives_calloc(nchans, sizeof(float *));
2942       nsamps = weed_layer_get_audio_length(layer);
2943       for (i = 0; i < nchans; i++) {
2944         adata[i] = lives_calloc(nsamps, sizeof(float));
2945         if (!adata[i]) {
2946           for (--i; i > 0; i--) lives_free(adata[i]);
2947           lives_free(adata);
2948           retval = FILTER_ERROR_MEMORY_ERROR;
2949           goto done_audio;
2950         }
2951       }
2952       weed_set_boolean_value(layer, WEED_LEAF_HOST_INPLACE, WEED_FALSE);
2953       weed_set_voidptr_array(channel, WEED_LEAF_AUDIO_DATA, nchans, (void **)adata);
2954       lives_free(adata);
2955     }
2956 
2957     weed_leaf_dup(channel, layer, WEED_LEAF_AUDIO_DATA_LENGTH);
2958     weed_leaf_dup(channel, layer, WEED_LEAF_AUDIO_RATE);
2959     weed_leaf_dup(channel, layer, WEED_LEAF_AUDIO_CHANNELS);
2960   }
2961 
2962   if (CURRENT_CLIP_IS_VALID) weed_set_double_value(inst, WEED_LEAF_FPS, cfile->pb_fps);
2963 
2964   //...finally we are ready to apply the filter
2965   retval = run_process_func(inst, tc, key);
2966 
2967   if (retval == FILTER_ERROR_INVALID_PLUGIN) {
2968     retval = FILTER_ERROR_INVALID_PLUGIN;
2969     goto done_audio;
2970   }
2971 
2972   // TODO - handle process errors (e.g. WEED_ERROR_PLUGIN_INVALID)
2973 
2974   // now we write our out channels back to layers
2975   for (i = 0; i < num_out_tracks; i++) {
2976     channel = get_enabled_channel(inst, i, FALSE);
2977     layer = layers[out_tracks[i] + nbtracks];
2978     if (weed_plant_has_leaf(channel, WEED_LEAF_AUDIO_DATA)) {
2979       /// free any old audio data in the layer, unless it's INPLACE
2980       if (weed_get_boolean_value(layer, WEED_LEAF_HOST_INPLACE, NULL) == WEED_FALSE
2981           && weed_get_boolean_value(layer, WEED_LEAF_HOST_KEEP_ADATA, NULL) == WEED_FALSE) {
2982         adata = (float **)weed_get_voidptr_array_counted(layer, WEED_LEAF_AUDIO_DATA, &nchans);
2983         for (j = 0; j < nchans; j++) lives_freep((void **)&adata[j]);
2984         lives_freep((void **)&adata);
2985       }
2986       weed_leaf_copy(layer, WEED_LEAF_AUDIO_DATA, channel, WEED_LEAF_AUDIO_DATA);
2987       /* g_print("setting 333dfff %d op layer for channel %p from chan %p, eg %f \n", in_tracks[i] + nbtracks, weed_get_voidptr_value(layer, "audio_data", NULL), weed_get_voidptr_value(channel, "audio_data", NULL), ((float *)(weed_get_voidptr_value(layer, "audio_data", NULL)))[0]); */
2988     }
2989   }
2990   for (i = 0; i < num_inc; i++) {
2991     if (weed_get_boolean_value(in_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) {
2992       weed_set_boolean_value(in_channels[i], WEED_LEAF_HOST_TEMP_DISABLED, WEED_FALSE);
2993     }
2994   }
2995 
2996 done_audio:
2997   lives_freep((void **)&in_tracks);
2998   lives_freep((void **)&out_tracks);
2999   lives_freep((void **)&in_channels);
3000   lives_freep((void **)&out_channels);
3001 
3002   return retval;
3003 }
3004 
3005 
3006 /**
3007    @brief apply an audio filter to a stack of audio tracks
3008 
3009     init_event may contain a pointer to the init_event in the event_list (if any)
3010     this is used to map tracks to the in and out channels of the filter indtance
3011 
3012     if NULL (e.g. during free playback) we create a fake channel map mapping layer 0 -> in, layer 0 -> out.
3013 
3014     abuf is a float ** array of audio, with each element being 1 audio channel (as in left/ right)
3015     grouped by track number
3016     nchans is number of audio channels (1 or 2 currently) in each track
3017     nbtracks is the number of backing tracks; track number will be -nbtracks <= track <= n
3018     track no. >= 0 is audio from a video track in multitrack, < 0 is a backing audio track (audio only, no video)
3019     nsamps is the sample size, arate the audio rate, tc the current timecode of the event_list or player
3020     and vis is a matrix of "visibility": 1 = front layer, full volume, 0 - rear layer, no volume, and a value in between
3021     can result from a transition.
3022 
3023     During rendering the audio render process feeds small chunks at a time to be processed, with the final result passing into
3024     the channel mixer (volume and pan)
3025 
3026     We set the values for the in and out channels, possibly disabling or enabling some.
3027     IF the instance needs reiniting (because od our changes and its filter / chantmpl flags) then we reinit it
3028 
3029     we runi the process_func and the output is returned to abuf, using the out_tracks to map the out channels
3030 */
weed_apply_audio_instance(weed_plant_t * init_event,weed_layer_t ** layers,int nbtracks,int nchans,int64_t nsamps,double arate,weed_timecode_t tc,double * vis)3031 lives_filter_error_t weed_apply_audio_instance(weed_plant_t *init_event, weed_layer_t **layers, int nbtracks, int nchans,
3032     int64_t nsamps, double arate, weed_timecode_t tc, double * vis) {
3033   lives_filter_error_t retval = FILTER_SUCCESS, retval2;
3034 
3035   weed_plant_t **in_channels = NULL, **out_channels = NULL;
3036 
3037   weed_plant_t *instance = NULL, *orig_inst, *filter;
3038   weed_plant_t *channel = NULL;
3039   weed_plant_t *ctmpl;
3040 
3041   int *in_tracks = NULL, *out_tracks = NULL;
3042 
3043   boolean needs_reinit = FALSE;
3044   boolean was_init_event = FALSE;
3045 
3046   int flags = 0, cflags;
3047   int key = -1;
3048   boolean rvary = FALSE, lvary = FALSE;
3049 
3050   int ntracks = 1;
3051   int numinchans = 0, numoutchans = 0, xnchans, xxnchans, xrate;
3052 
3053   register int i;
3054 
3055   // caller passes an init_event from event list, or an instance (for realtime mode)
3056 
3057   if (WEED_PLANT_IS_FILTER_INSTANCE(init_event)) {
3058     // for realtime, we pass a single instance instead of init_event
3059 
3060     instance = init_event; // needs to be refcounted
3061 
3062     in_channels = weed_get_plantptr_array(instance, WEED_LEAF_IN_CHANNELS, NULL);
3063     out_channels = weed_get_plantptr_array(instance, WEED_LEAF_OUT_CHANNELS, NULL);
3064 
3065     if (!in_channels) {
3066       if (!out_channels && weed_plant_has_leaf(instance, WEED_LEAF_OUT_PARAMETERS)) {
3067         // plugin has no in channels and no out channels, but if it has out paramters then it must be a data processing module
3068 
3069         // if the data feeds into audio effects then we run it now, otherwise we will run it during the video cycle
3070         if (!feeds_to_audio_filters(key, rte_key_getmode(key + 1))) return FILTER_ERROR_NO_IN_CHANNELS;
3071 
3072         // otherwise we just run the process_func() and return
3073 
3074         if (CURRENT_CLIP_IS_VALID)  weed_set_double_value(instance, WEED_LEAF_FPS, cfile->pb_fps);
3075         retval = run_process_func(instance, tc, key);
3076         return retval;
3077       }
3078       lives_free(out_channels);
3079       return FILTER_ERROR_NO_IN_CHANNELS;
3080     }
3081 
3082     lives_free(in_channels);
3083     lives_free(out_channels);
3084     in_channels = out_channels = NULL;
3085 
3086     // set init_event to NULL before passing it to the inner function
3087     init_event = NULL;
3088 
3089     in_tracks = (int *)lives_calloc(1, sizint);
3090     out_tracks = (int *)lives_calloc(1, sizint);
3091   } else {
3092     // when processing an event list, we pass an init_event
3093     was_init_event = TRUE;
3094     if (weed_plant_has_leaf(init_event, WEED_LEAF_HOST_TAG)) {
3095       char *keystr = weed_get_string_value(init_event, WEED_LEAF_HOST_TAG, NULL);
3096       key = atoi(keystr);
3097       lives_freep((void **)&keystr);
3098     } else return FILTER_ERROR_INVALID_INIT_EVENT;
3099 
3100     in_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_IN_TRACKS, &ntracks);
3101 
3102     if (weed_plant_has_leaf(init_event, WEED_LEAF_OUT_TRACKS))
3103       out_tracks = weed_get_int_array(init_event, WEED_LEAF_OUT_TRACKS, NULL);
3104 
3105     // check instance exists, and interpolate parameters
3106 
3107     if (rte_key_valid(key + 1, FALSE)) {
3108       if ((instance = weed_instance_obtain(key, key_modes[key])) == NULL) {
3109         lives_freep((void **)&in_tracks);
3110         lives_freep((void **)&out_tracks);
3111         return FILTER_ERROR_INVALID_INSTANCE;
3112       }
3113       if (mainw->pchains && mainw->pchains[key]) {
3114         /// interpolate the params, if we can get a mutex lock on the inst
3115         if (!interpolate_params(instance, mainw->pchains[key], tc)) {
3116           weed_instance_unref(instance);
3117           lives_freep((void **)&in_tracks);
3118           lives_freep((void **)&out_tracks);
3119           return FILTER_ERROR_INTERPOLATION_FAILED;
3120         }
3121       }
3122     } else {
3123       lives_freep((void **)&in_tracks);
3124       lives_freep((void **)&out_tracks);
3125       return FILTER_ERROR_INVALID_INIT_EVENT;
3126     }
3127   }
3128 
3129   orig_inst = instance;
3130 
3131 audinst1:
3132   lives_freep((void **)&in_channels);
3133   lives_freep((void **)&out_channels);
3134 
3135   in_channels = weed_get_plantptr_array_counted(instance, WEED_LEAF_IN_CHANNELS, &numinchans);
3136   out_channels = weed_get_plantptr_array_counted(instance, WEED_LEAF_OUT_CHANNELS, &numoutchans);
3137 
3138   retval = enable_disable_channels(instance, TRUE, in_tracks, ntracks, nbtracks, layers);
3139   // in theory we should check out channels also, but for now we only load audio filters with one mandatory out
3140 
3141   if (retval != FILTER_SUCCESS) goto audret1;
3142 
3143   filter = weed_instance_get_filter(instance, FALSE);
3144   flags = weed_filter_get_flags(filter);
3145 
3146   if (flags & WEED_FILTER_AUDIO_RATES_MAY_VARY) rvary = TRUE;
3147   if (flags & WEED_FILTER_CHANNEL_LAYOUTS_MAY_VARY) lvary = TRUE;
3148 
3149   if (vis && vis[0] < 0. && in_tracks[0] <= -nbtracks) {
3150     /// first layer comes from ascrap file; do not apply any effects except the final audio mixer
3151     if (numinchans == 1 && numoutchans == 1 && !(flags & WEED_FILTER_IS_CONVERTER)) {
3152       retval = FILTER_ERROR_IS_SCRAP_FILE;
3153       goto audret1;
3154     }
3155   }
3156 
3157   for (i = 0; i < numinchans; i++) {
3158     if ((channel = in_channels[i]) == NULL) continue;
3159     if (weed_channel_is_disabled(channel) ||
3160         weed_get_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) continue;
3161 
3162     xnchans = nchans; // preferred value
3163     ctmpl = weed_channel_get_template(channel);
3164     cflags = weed_chantmpl_get_flags(ctmpl);
3165     // TODO ** - can be list
3166     if (lvary && weed_plant_has_leaf(ctmpl, WEED_LEAF_AUDIO_CHANNELS))
3167       xnchans = weed_get_int_value(ctmpl, WEED_LEAF_AUDIO_CHANNELS, NULL);
3168     else if (weed_plant_has_leaf(filter, WEED_LEAF_MAX_AUDIO_CHANNELS)) {
3169       xxnchans = weed_get_int_value(filter, WEED_LEAF_MAX_AUDIO_CHANNELS, NULL);
3170       if (xxnchans > 0 && xxnchans < nchans) xnchans = xxnchans;
3171     }
3172     if (xnchans != nchans) {
3173       retval = FILTER_ERROR_TEMPLATE_MISMATCH;
3174       goto audret1;
3175     }
3176     if (weed_get_int_value(channel, WEED_LEAF_AUDIO_CHANNELS, NULL) != nchans
3177         && (cflags & WEED_CHANNEL_REINIT_ON_LAYOUT_CHANGE))
3178       needs_reinit = TRUE;
3179     else weed_set_int_value(channel, WEED_LEAF_AUDIO_CHANNELS, nchans);
3180 
3181     //if ((weed_plant_has_leaf(ctmpl, WEED_LEAF_AUDIO_CHANNEL_LAYOUT) {
3182     // TODO - check layouts if present, make sure it supports mono / stereo
3183 
3184     xrate = arate;
3185     // TODO : can be list
3186     if (rvary && weed_plant_has_leaf(ctmpl, WEED_LEAF_AUDIO_RATE))
3187       xrate = weed_get_int_value(ctmpl, WEED_LEAF_AUDIO_RATE, NULL);
3188     else if (weed_plant_has_leaf(filter, WEED_LEAF_AUDIO_RATE))
3189       xrate = weed_get_int_value(filter, WEED_LEAF_AUDIO_RATE, NULL);
3190     if (arate != xrate) {
3191       // TODO - resample
3192       retval = FILTER_ERROR_TEMPLATE_MISMATCH;
3193       goto audret1;
3194     }
3195 
3196     if (weed_get_int_value(channel, WEED_LEAF_AUDIO_RATE, NULL) != arate) {
3197       if (cflags & WEED_CHANNEL_REINIT_ON_RATE_CHANGE) {
3198         needs_reinit = TRUE;
3199       }
3200     }
3201 
3202     weed_set_int_value(channel, WEED_LEAF_AUDIO_RATE, arate);
3203   }
3204 
3205   for (i = 0; i < numoutchans; i++) {
3206     if ((channel = out_channels[i]) == NULL) continue;
3207     if (weed_channel_is_disabled(channel) ||
3208         weed_get_boolean_value(channel, WEED_LEAF_HOST_TEMP_DISABLED, NULL) == WEED_TRUE) continue;
3209     xnchans = nchans; // preferred value
3210     ctmpl = weed_channel_get_template(channel);
3211     cflags = weed_chantmpl_get_flags(ctmpl);
3212     // TODO ** - can be list
3213     if (lvary && weed_plant_has_leaf(ctmpl, WEED_LEAF_AUDIO_CHANNELS))
3214       xnchans = weed_get_int_value(ctmpl, WEED_LEAF_AUDIO_CHANNELS, NULL);
3215     else if (weed_plant_has_leaf(filter, WEED_LEAF_MAX_AUDIO_CHANNELS)) {
3216       xxnchans = weed_get_int_value(filter, WEED_LEAF_MAX_AUDIO_CHANNELS, NULL);
3217       if (xxnchans > 0 && xxnchans < nchans) xnchans = xxnchans;
3218     }
3219     if (xnchans != nchans) {
3220       retval = FILTER_ERROR_TEMPLATE_MISMATCH;
3221       goto audret1;
3222     }
3223     if (weed_get_int_value(channel, WEED_LEAF_AUDIO_CHANNELS, NULL) != nchans
3224         && (cflags & WEED_CHANNEL_REINIT_ON_LAYOUT_CHANGE))
3225       needs_reinit = TRUE;
3226     else weed_set_int_value(channel, WEED_LEAF_AUDIO_CHANNELS, nchans);
3227 
3228     //if ((weed_plant_has_leaf(ctmpl, WEED_LEAF_AUDIO_CHANNEL_LAYOUT) {
3229     // TODO - check layouts if present, make sure it supports mono / stereo
3230 
3231     xrate = arate;
3232     // TODO : can be list
3233     if (rvary && weed_plant_has_leaf(ctmpl, WEED_LEAF_AUDIO_RATE))
3234       xrate = weed_get_int_value(ctmpl, WEED_LEAF_AUDIO_RATE, NULL);
3235     else if (weed_plant_has_leaf(filter, WEED_LEAF_AUDIO_RATE))
3236       xrate = weed_get_int_value(filter, WEED_LEAF_AUDIO_RATE, NULL);
3237     if (arate != xrate) {
3238       // TODO - resample
3239       retval = FILTER_ERROR_TEMPLATE_MISMATCH;
3240       goto audret1;
3241     }
3242 
3243     if (weed_get_int_value(channel, WEED_LEAF_AUDIO_RATE, NULL) != arate) {
3244       if (cflags & WEED_CHANNEL_REINIT_ON_RATE_CHANGE) {
3245         needs_reinit = TRUE;
3246       }
3247     }
3248 
3249     weed_set_int_value(channel, WEED_LEAF_AUDIO_RATE, arate);
3250   }
3251 
3252   if (needs_reinit) {
3253     // - deinit inst
3254     // mutex locked
3255     if (key != -1) filter_mutex_lock(key);
3256     weed_call_deinit_func(instance);
3257     if (key != -1) filter_mutex_unlock(key);
3258 
3259     // - init inst
3260     if (weed_plant_has_leaf(filter, WEED_LEAF_INIT_FUNC)) {
3261       weed_init_f init_func = (weed_init_f)weed_get_funcptr_value(filter, WEED_LEAF_INIT_FUNC, NULL);
3262       if (init_func) {
3263         char *cwd = cd_to_plugin_dir(filter);
3264         if ((*init_func)(instance) != WEED_SUCCESS) {
3265           key_to_instance[key][key_modes[key]] = NULL;
3266           lives_chdir(cwd, FALSE);
3267           lives_free(cwd);
3268           lives_freep((void **)&in_channels);
3269           lives_freep((void **)&out_channels);
3270           retval = FILTER_ERROR_COULD_NOT_REINIT;
3271           goto audret1;
3272         }
3273         lives_chdir(cwd, FALSE);
3274         lives_free(cwd);
3275       }
3276     }
3277     weed_set_boolean_value(instance, WEED_LEAF_HOST_INITED, WEED_TRUE);
3278     weed_set_boolean_value(instance, WEED_LEAF_HOST_UNUSED, WEED_TRUE);
3279     retval = FILTER_INFO_REINITED;
3280   }
3281 
3282   // apply visibility mask to volume values
3283   if (vis && (flags & WEED_FILTER_IS_CONVERTER)) {
3284     int vmaster = get_master_vol_param(filter, FALSE);
3285     if (vmaster != -1) {
3286       int nvals;
3287       weed_plant_t **in_params = weed_instance_get_in_params(instance, NULL);
3288       double *fvols = weed_get_double_array_counted(in_params[vmaster], WEED_LEAF_VALUE, &nvals);
3289       for (i = 0; i < nvals; i++) {
3290         fvols[i] = fvols[i] * vis[in_tracks[i] + nbtracks];
3291         if (vis[in_tracks[i] + nbtracks] < 0.) fvols[i] = -fvols[i];
3292       }
3293       if (!filter_mutex_trylock(key)) {
3294         weed_set_double_array(in_params[vmaster], WEED_LEAF_VALUE, nvals, fvols);
3295         filter_mutex_unlock(key);
3296         //set_copy_to(instance, vmaster, TRUE);
3297       }
3298       lives_freep((void **)&fvols);
3299       lives_freep((void **)&in_params);
3300     }
3301   }
3302 
3303   retval2 = weed_apply_audio_instance_inner(instance, init_event, layers, tc, nbtracks);
3304   if (retval == FILTER_SUCCESS) retval = retval2;
3305 
3306   if (retval2 == FILTER_SUCCESS && was_init_event) {
3307     // handle compound filters
3308     if ((instance = get_next_compound_inst(instance)) != NULL) goto audinst1;
3309   }
3310 
3311   // TODO - handle invalid, needs_reinit etc.
3312 
3313   if (was_init_event) weed_instance_unref(orig_inst);
3314 
3315 audret1:
3316   lives_freep((void **)&in_channels);
3317   lives_freep((void **)&out_channels);
3318 
3319   lives_freep((void **)&in_tracks);
3320   lives_freep((void **)&out_tracks);
3321 
3322   return retval;
3323 }
3324 
3325 
3326 /**
3327    @brief - this is called during rendering - we will have previously received a filter_map event and now we apply this to layers
3328 
3329   layers will be a NULL terminated array of channels, each with two extra leaves: clip and frame
3330   clip corresponds to a LiVES file in mainw->files. WEED_LEAF_PIXEL_DATA will initially be NULL, we will pull this as necessary
3331   and the effect output is written back to the layers
3332 
3333   a frame number of 0 indicates a blank frame;
3334   if an effect gets one of these it will not process (except if the channel is optional,
3335   in which case the channel is disabled); the same is true for invalid track numbers -
3336   hence disabled channels which do not have disabled in the template are re-enabled when a frame is available
3337 
3338   after all processing, we generally display/output the first non-NULL layer.
3339   If all layers are NULL we generate a blank frame
3340 
3341   size and palettes of resulting layers may change during this
3342 
3343   the channel sizes are set by the filter: all channels are all set to the size of the largest input layer.
3344   (We attempt to do this, but some channels have fixed sizes).
3345 **/
weed_apply_filter_map(weed_plant_t ** layers,weed_plant_t * filter_map,weed_timecode_t tc,void *** pchains)3346 static void weed_apply_filter_map(weed_plant_t **layers, weed_plant_t *filter_map, weed_timecode_t tc, void ***pchains) {
3347   weed_plant_t *instance, *orig_inst;
3348   weed_plant_t *init_event;
3349   lives_filter_error_t retval;
3350 
3351   char *keystr;
3352 
3353   void **init_events;
3354 
3355   weed_error_t filter_error;
3356   int key, num_inst;
3357 
3358   boolean needs_reinit;
3359 
3360   if (!filter_map || !weed_plant_has_leaf(filter_map, WEED_LEAF_INIT_EVENTS) ||
3361       !weed_get_voidptr_value(filter_map, WEED_LEAF_INIT_EVENTS, NULL)) return;
3362 
3363   init_events = weed_get_voidptr_array_counted(filter_map, WEED_LEAF_INIT_EVENTS, &num_inst);
3364 
3365   for (int i = 0; i < num_inst; i++) {
3366     init_event = (weed_plant_t *)init_events[i];
3367 
3368     if (weed_plant_has_leaf(init_event, WEED_LEAF_HOST_TAG)) {
3369       keystr = weed_get_string_value(init_event, WEED_LEAF_HOST_TAG, NULL);
3370       key = atoi(keystr);
3371       lives_free(keystr);
3372       if (rte_key_valid(key + 1, FALSE)) {
3373         if ((instance = weed_instance_obtain(key, key_modes[key])) == NULL) continue;
3374 
3375         if (is_pure_audio(instance, FALSE)) {
3376           weed_instance_unref(instance);
3377           continue; // audio effects are applied in the audio renderer
3378         }
3379 
3380         orig_inst = instance;
3381 
3382         if (!LIVES_IS_PLAYING && mainw->multitrack && mainw->multitrack->current_rfx
3383             && mainw->multitrack->current_rfx->source
3384             && (mainw->multitrack->solo_inst || instance == mainw->multitrack->current_rfx->source)) {
3385           if (instance == mainw->multitrack->current_rfx->source) {
3386             void **pchain = mt_get_pchain();
3387             // interpolation can be switched of by setting mainw->no_interp
3388             if (!mainw->no_interp && pchain) {
3389               interpolate_params(instance, pchain, tc); // interpolate parameters for preview
3390             }
3391           } else {
3392             if (mainw->multitrack->solo_inst) {
3393               weed_instance_unref(instance);
3394               continue;
3395             }
3396           }
3397         } else {
3398           int idx;
3399           char *filter_hash;
3400           boolean is_valid = FALSE;
3401           if (!weed_plant_has_leaf(init_events[i], WEED_LEAF_OUT_TRACKS)
3402               || !weed_plant_has_leaf(init_events[i], WEED_LEAF_IN_TRACKS)) continue;
3403           if (mainw->multitrack && mainw->multitrack->solo_inst && mainw->multitrack->init_event
3404               && !LIVES_IS_PLAYING && mainw->multitrack->init_event != init_events[i]) continue;
3405           filter_hash = weed_get_string_value(init_events[i], WEED_LEAF_FILTER, NULL);
3406           if ((idx = weed_get_idx_for_hashname(filter_hash, TRUE)) != -1) {
3407             weed_plant_t *filter = get_weed_filter(idx);
3408             if (has_video_chans_in(filter, FALSE) && has_video_chans_out(filter, FALSE)) {
3409               int nintracks, *in_tracks = weed_get_int_array_counted(init_events[i], WEED_LEAF_IN_TRACKS, &nintracks);
3410               for (register int j = 0; j < nintracks; j++) {
3411                 if (j >= mainw->num_tracks) break;
3412                 if (mainw->active_track_list[in_tracks[j]] > 0) {
3413                   is_valid = TRUE;
3414                   break;
3415                 }
3416               }
3417               lives_free(in_tracks);
3418             }
3419           }
3420           lives_free(filter_hash);
3421           /// avoid applying to non-active tracks
3422           if (!is_valid) continue;
3423 
3424           if (pchains && pchains[key]) {
3425             interpolate_params(instance, pchains[key], tc); // interpolate parameters during playback
3426           }
3427         }
3428         /*
3429           // might be needed for multitrack ???
3430           if (mainw->pconx!=NULL) {
3431           int key=i;
3432           int mode=key_modes[i];
3433           if (weed_plant_has_leaf(instance,WEED_LEAF_HOST_MODE)) {
3434           key=weed_get_int_value(instance,WEED_LEAF_HOST_KEY,&error);
3435           mode=weed_get_int_value(instance,WEED_LEAF_HOST_MODE,&error);
3436           }
3437           // chain any data pipelines
3438           pconx_chain_data(key,mode);
3439           }*/
3440 
3441 apply_inst2:
3442 
3443         if (weed_plant_has_leaf(instance, WEED_LEAF_HOST_NEXT_INSTANCE)) {
3444           // chain any internal data pipelines for compound fx
3445           needs_reinit = pconx_chain_data_internal(instance);
3446           if (needs_reinit) {
3447             if ((retval = weed_reinit_effect(instance, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
3448               weed_instance_unref(orig_inst);
3449               if (!LIVES_IS_PLAYING && mainw->multitrack && mainw->multitrack->solo_inst &&
3450                   orig_inst == mainw->multitrack->solo_inst) break;
3451               continue;
3452             }
3453           }
3454         }
3455 
3456         //if (LIVES_IS_PLAYING)
3457         filter_error = weed_apply_instance(instance, init_event, layers, mainw->pwidth, mainw->pheight, tc);
3458 
3459         //filter_error = weed_apply_instance(instance, init_event, layers, 0, 0, tc);
3460 
3461         if (filter_error == WEED_SUCCESS && (instance = get_next_compound_inst(instance)) != NULL) goto apply_inst2;
3462         if (filter_error == WEED_ERROR_REINIT_NEEDED) {
3463           if ((retval = weed_reinit_effect(instance, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
3464             weed_instance_unref(orig_inst);
3465             if (!LIVES_IS_PLAYING && mainw->multitrack && mainw->multitrack->solo_inst &&
3466                 orig_inst == mainw->multitrack->solo_inst) break;
3467             continue;
3468           }
3469         }
3470         //if (filter_error!=FILTER_SUCCESS) lives_printerr("Render error was %d\n",filter_error);
3471         if (!LIVES_IS_PLAYING && mainw->multitrack && mainw->multitrack->solo_inst &&
3472             orig_inst == mainw->multitrack->solo_inst) {
3473           weed_instance_unref(orig_inst);
3474           break;
3475         }
3476         weed_instance_unref(orig_inst);
3477       }
3478     }
3479   }
3480   lives_freep((void **)&init_events);
3481 }
3482 
3483 
weed_apply_effects(weed_plant_t ** layers,weed_plant_t * filter_map,weed_timecode_t tc,int opwidth,int opheight,void *** pchains)3484 weed_plant_t *weed_apply_effects(weed_plant_t **layers, weed_plant_t *filter_map, weed_timecode_t tc,
3485                                  int opwidth, int opheight, void ***pchains) {
3486   // given a stack of layers, a filter map, a timecode and possibly paramater chains
3487   // apply the effects in the filter map, and return a single layer as the result
3488 
3489   // if all goes wrong we return a blank 4x4 RGB24 layer (TODO - return a NULL ?)
3490 
3491   // returned layer can be of any width,height,palette
3492   // caller should free all input layers, WEED_LEAF_PIXEL_DATA of all non-returned layers is free()d here
3493 
3494   weed_plant_t *filter, *instance, *orig_inst, *instance2, *layer;
3495   lives_filter_error_t filter_error;
3496 
3497   boolean needs_reinit;
3498 
3499   int output = -1;
3500   int clip;
3501   int easeval = 0;
3502 
3503   int i;
3504 
3505   if (mainw->is_rendering && !(mainw->proc_ptr && mainw->preview)) {
3506     // rendering from multitrack
3507     if (filter_map && layers[0]) {
3508       weed_apply_filter_map(layers, filter_map, tc, pchains);
3509     }
3510   }
3511 
3512   // free playback: we will have here only one or two layers, and no filter_map.
3513   // Effects are applied in key order, in tracks are 0 and 1, out track is 0
3514   else {
3515     weed_plant_t *gui;
3516     for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
3517       if (rte_key_valid(i + 1, TRUE)) {
3518         if (!(rte_key_is_enabled(1 + i))) {
3519           // if anything is connected to ACTIVATE, the fx may be activated
3520           pconx_chain_data(i, key_modes[i], FALSE);
3521         }
3522         if (rte_key_is_enabled(1 + i)) {
3523           mainw->osc_block = TRUE;
3524           if ((instance = weed_instance_obtain(i, key_modes[i])) == NULL) {
3525             mainw->osc_block = FALSE;
3526             continue;
3527           }
3528           filter = weed_instance_get_filter(instance, TRUE);
3529 
3530           if (is_pure_audio(filter, TRUE)) {
3531             weed_instance_unref(instance);
3532             continue;
3533           }
3534           gui = weed_instance_get_gui(instance, FALSE);
3535           if (weed_get_int_value(gui, WEED_LEAF_EASE_OUT, NULL) > 0) {
3536             // if the plugin is easing out, check if it finished
3537             if (!weed_plant_has_leaf(instance, WEED_LEAF_AUTO_EASING)) { // if auto_Easing then we'll deinit it on the event_list
3538               if (!weed_get_int_value(gui, WEED_LEAF_EASE_OUT_FRAMES, NULL)) {
3539                 // easing finished, deinit it
3540                 uint64_t new_rte = GU641 << (i);
3541                 // record
3542                 if (init_events[i]) {
3543                   // if we are recording, mark the number of frames to ease out
3544                   // we'll need to repeat the same process during preview / rendering
3545                   // - when we hit the init_event, we'll find the deinit, then work back x frames and mark easing start
3546                   weed_set_int_value(init_events[i], WEED_LEAF_EASE_OUT,
3547                                      weed_get_int_value(instance, WEED_LEAF_EASE_OUT, NULL));
3548                 }
3549                 weed_instance_unref(instance);
3550                 filter_mutex_lock(i);
3551                 weed_deinit_effect(i);
3552                 if (mainw->rte & new_rte) {
3553                   mainw->rte ^= new_rte;
3554                   if (rte_window) rtew_set_keych(i, FALSE);
3555                   if (mainw->ce_thumbs) ce_thumbs_set_keych(i, FALSE);
3556                 }
3557                 filter_mutex_unlock(i);
3558                 continue;
3559 		// *INDENT-OFF*
3560               }}}
3561 	  // *INDENT-ON*
3562 
3563           if (mainw->pchains && mainw->pchains[i]) {
3564             if (!filter_mutex_trylock(i)) {
3565               interpolate_params(instance, mainw->pchains[i], tc); // interpolate parameters during preview
3566               filter_mutex_unlock(i);
3567             }
3568           }
3569           //filter = weed_instance_get_filter(instance, TRUE);
3570 
3571           // TODO *** enable this, and apply pconx to audio gens in audio.c
3572           if (!is_pure_audio(filter, TRUE)) {
3573             if (mainw->pconx && !(mainw->preview || mainw->is_rendering)) {
3574               // chain any data pipelines
3575               needs_reinit = pconx_chain_data(i, key_modes[i], FALSE);
3576 
3577               // if anything is connected to ACTIVATE, the fx may be activated
3578               if ((instance2 = weed_instance_obtain(i, key_modes[i])) == NULL) {
3579                 weed_instance_unref(instance);
3580                 continue;
3581               }
3582               weed_instance_unref(instance);
3583               instance = instance2;
3584               if (needs_reinit) {
3585                 if ((filter_error = weed_reinit_effect(instance, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
3586                   weed_instance_unref(instance);
3587                   continue;
3588 		// *INDENT-OFF*
3589                 }}}}
3590 	  // *INDENT-ON*
3591 
3592           orig_inst = instance;
3593 
3594 apply_inst3:
3595 
3596           if (weed_plant_has_leaf(instance, WEED_LEAF_HOST_NEXT_INSTANCE)) {
3597             // chain any internal data pipelines for compound fx
3598             needs_reinit = pconx_chain_data_internal(instance);
3599             if (needs_reinit) {
3600               if ((filter_error = weed_reinit_effect(instance, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
3601                 weed_instance_unref(instance);
3602                 continue;
3603 		// *INDENT-OFF*
3604               }}}
3605 	  // *INDENT-ON*
3606 
3607           filter_error = weed_apply_instance(instance, NULL, layers, opwidth, opheight, tc);
3608 
3609           if (easeval > 0 && !weed_plant_has_leaf(orig_inst, WEED_LEAF_AUTO_EASING)) {
3610             // if the plugin is supposed to be easing out, make sure it is really
3611             weed_plant_t *gui = weed_instance_get_gui(orig_inst, FALSE);
3612             if (gui) {
3613               int xeaseval = weed_get_int_value(gui, WEED_LEAF_EASE_OUT_FRAMES, NULL), myeaseval;
3614               myeaseval = weed_get_int_value(instance, WEED_LEAF_HOST_EASE_OUT_COUNT, NULL);
3615               if (xeaseval > myeaseval) {
3616                 uint64_t new_rte = GU641 << (i);
3617                 filter_mutex_lock(i);
3618                 weed_instance_unref(orig_inst);
3619                 if (mainw->rte & new_rte) {
3620                   mainw->rte ^= new_rte;
3621                   if (rte_window) rtew_set_keych(i, FALSE);
3622                   if (mainw->ce_thumbs) ce_thumbs_set_keych(i, FALSE);
3623                 }
3624                 weed_deinit_effect(i);
3625                 filter_mutex_unlock(i);
3626                 continue;
3627               }
3628               // count how many frames to ease out
3629               weed_set_int_value(instance, WEED_LEAF_HOST_EASE_OUT_COUNT,
3630                                  weed_get_int_value(instance, WEED_LEAF_HOST_EASE_OUT_COUNT, NULL) + 1);
3631             }
3632           }
3633           if (filter_error == FILTER_ERROR_NEEDS_REINIT) {
3634             // TODO...
3635           }
3636           if (filter_error == FILTER_INFO_REINITED)
3637             update_widget_vis(NULL, i, key_modes[i]);
3638           //#define DEBUG_RTE
3639 #ifdef DEBUG_RTE
3640           if (filter_error != FILTER_SUCCESS) lives_printerr("Render error was %d\n", filter_error);
3641 #endif
3642           if (filter_error == FILTER_SUCCESS && (instance = get_next_compound_inst(instance))) goto apply_inst3;
3643 
3644           if (mainw->pconx && (filter_error == FILTER_SUCCESS || filter_error == FILTER_INFO_REINITED
3645                                || filter_error == FILTER_INFO_REDRAWN)) {
3646             pconx_chain_data_omc(orig_inst, i, key_modes[i]);
3647           }
3648           weed_instance_unref(orig_inst);
3649 	  // *INDENT-OFF*
3650         }}}}
3651   // *INDENT-ON*
3652 
3653   // TODO - set mainw->vpp->play_params from connected out params and out alphas
3654 
3655   if (!mainw->is_rendering) {
3656     mainw->osc_block = FALSE;
3657   }
3658 
3659   // caller should free all layers, but here we will free all other pixel_data
3660 
3661   for (i = 0; layers[i]; i++) {
3662     if (layers[i] == mainw->blend_layer) mainw->blend_layer = NULL;
3663 
3664     if ((mainw->multitrack && i == mainw->multitrack->preview_layer) || ((!mainw->multitrack ||
3665         mainw->multitrack->preview_layer < 0) &&
3666         ((weed_get_voidptr_value(layers[i], WEED_LEAF_PIXEL_DATA, NULL)) ||
3667          (weed_get_int_value(layers[i], WEED_LEAF_FRAME, NULL) != 0 &&
3668           (LIVES_IS_PLAYING || !mainw->multitrack || !mainw->multitrack->current_rfx ||
3669            (!mainw->multitrack->init_event || tc < get_event_timecode(mainw->multitrack->init_event) ||
3670             (mainw->multitrack->init_event == mainw->multitrack->avol_init_event) ||
3671             tc > get_event_timecode(weed_get_plantptr_value
3672                                     (mainw->multitrack->init_event, WEED_LEAF_DEINIT_EVENT, NULL)))))))) {
3673       if (output != -1 || weed_get_int_value(layers[i], WEED_LEAF_CLIP, NULL) == -1) {
3674         if (!weed_plant_has_leaf(layers[i], WEED_LEAF_PIXEL_DATA)) continue;
3675         check_layer_ready(layers[i]);
3676         weed_layer_pixel_data_free(layers[i]);
3677       } else output = i;
3678     } else {
3679       if (!weed_plant_has_leaf(layers[i], WEED_LEAF_PIXEL_DATA)) continue;
3680       check_layer_ready(layers[i]);
3681       weed_layer_pixel_data_free(layers[i]);
3682     }
3683   }
3684 
3685   if (output == -1) {
3686     // blank frame - e.g. for multitrack
3687     return create_blank_layer(NULL, NULL, opwidth, opheight, WEED_PALETTE_END);
3688   }
3689 
3690   layer = layers[output];
3691   clip = weed_get_int_value(layer, WEED_LEAF_CLIP, NULL);
3692 
3693   // frame is pulled uneffected here. TODO: Try to pull at target output palette
3694   if (!weed_get_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, NULL)) {
3695     check_layer_ready(layer);
3696     if (!pull_frame_at_size(layer, get_image_ext_for_type(mainw->files[clip]->img_type), tc, opwidth, opheight,
3697                             WEED_PALETTE_END)) {
3698       char *msg = lives_strdup_printf("weed_apply_effects created empty pixel_data at tc %ld, map was %p, clip = %d, frame = %d",
3699                                       tc, filter_map, clip, weed_get_int_value(layer, WEED_LEAF_FRAME, NULL));
3700       LIVES_WARN(msg);
3701       lives_free(msg);
3702       create_blank_layer(layer, get_image_ext_for_type(mainw->files[clip]->img_type), opwidth, opheight, WEED_PALETTE_END);
3703     }
3704   }
3705   return layer;
3706 }
3707 
3708 
weed_apply_audio_effects(weed_plant_t * filter_map,weed_layer_t ** layers,int nbtracks,int nchans,int64_t nsamps,double arate,weed_timecode_t tc,double * vis)3709 void weed_apply_audio_effects(weed_plant_t *filter_map, weed_layer_t **layers, int nbtracks, int nchans, int64_t nsamps,
3710                               double arate, weed_timecode_t tc, double * vis) {
3711   int i, num_inst, error;
3712   void **init_events;
3713   weed_plant_t *init_event, *filter;
3714   char *fhash;
3715 
3716   // this is called during rendering - we will have previously received a
3717   // filter_map event and now we apply this to audio (abuf)
3718   // abuf will be a NULL terminated array of float audio
3719 
3720   // the results of abuf[0] and abuf[1] (for stereo) will be written to fileno
3721   if (!filter_map || !weed_plant_has_leaf(filter_map, WEED_LEAF_INIT_EVENTS) ||
3722       !weed_get_voidptr_value(filter_map, WEED_LEAF_INIT_EVENTS, NULL)) {
3723     return;
3724   }
3725   mainw->pchains = get_event_pchains();
3726   init_events = weed_get_voidptr_array_counted(filter_map, WEED_LEAF_INIT_EVENTS, &num_inst);
3727   for (i = 0; i < num_inst; i++) {
3728     init_event = (weed_plant_t *)init_events[i];
3729     fhash = weed_get_string_value(init_event, WEED_LEAF_FILTER, &error);
3730     filter = get_weed_filter(weed_get_idx_for_hashname(fhash, TRUE));
3731     lives_freep((void **)&fhash);
3732     if (has_audio_chans_in(filter, FALSE) && !has_video_chans_in(filter, FALSE) && !has_video_chans_out(filter, FALSE) &&
3733         has_audio_chans_out(filter, FALSE)) {
3734       weed_apply_audio_instance(init_event, layers, nbtracks, nchans, nsamps, arate, tc, vis);
3735     }
3736     // TODO *** - also run any pure data processing filters which feed into audio filters
3737   }
3738   lives_freep((void **)&init_events);
3739   mainw->pchains = NULL;
3740 }
3741 
3742 
weed_apply_audio_effects_rt(weed_layer_t * alayer,weed_timecode_t tc,boolean analysers_only,boolean is_audio_thread)3743 void weed_apply_audio_effects_rt(weed_layer_t *alayer, weed_timecode_t tc, boolean analysers_only, boolean is_audio_thread) {
3744   weed_plant_t *instance, *filter, *orig_inst, *new_inst;
3745   lives_filter_error_t filter_error;
3746   weed_layer_t *layers[1];
3747   boolean needs_reinit;
3748   int nsamps, arate, nchans;
3749 
3750   // free playback: apply any audio filters or analysers (but not generators)
3751   // Effects are applied in key order
3752 
3753   if (!alayer) return;
3754 
3755   nchans = weed_layer_get_naudchans(alayer);
3756   arate = weed_layer_get_audio_rate(alayer);
3757   nsamps = weed_layer_get_audio_length(alayer);
3758 
3759   for (int i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
3760     if (rte_key_valid(i + 1, TRUE)) {
3761       if (!(rte_key_is_enabled(1 + i))) {
3762         // if anything is connected to ACTIVATE, the fx may be activated
3763         if (is_audio_thread) pconx_chain_data(i, key_modes[i], TRUE);
3764       }
3765       if (rte_key_is_enabled(1 + i)) {
3766         mainw->osc_block = TRUE;
3767 
3768         if ((orig_inst = instance = weed_instance_obtain(i, key_modes[i])) == NULL) {
3769           mainw->osc_block = FALSE;
3770           continue;
3771         }
3772 
3773         filter = weed_instance_get_filter(instance, FALSE);
3774 
3775         if (!has_audio_chans_in(filter, FALSE) || has_video_chans_in(filter, FALSE) || has_video_chans_out(filter, FALSE)) {
3776           weed_instance_unref(instance);
3777           mainw->osc_block = FALSE;
3778           continue;
3779         }
3780 
3781         if (analysers_only && has_audio_chans_out(filter, FALSE)) {
3782           weed_instance_unref(instance);
3783           continue;
3784         }
3785 
3786         if (mainw->pchains && mainw->pchains[i]) {
3787           if (!filter_mutex_trylock(i)) {
3788             interpolate_params(instance, mainw->pchains[i], tc); // interpolate parameters during preview
3789             filter_mutex_unlock(i);
3790           }
3791         }
3792         if (mainw->pconx  && is_audio_thread) {
3793           // chain any data pipelines
3794 
3795           needs_reinit = pconx_chain_data(i, key_modes[i], TRUE);
3796 
3797           // if anything is connected to ACTIVATE, the fx may be deactivated
3798           if ((new_inst = weed_instance_obtain(i, key_modes[i])) == NULL) {
3799             weed_instance_unref(instance);
3800             mainw->osc_block = FALSE;
3801             continue;
3802           }
3803 
3804           weed_instance_unref(instance);
3805           instance = new_inst;
3806 
3807           if (needs_reinit) {
3808             if ((filter_error = weed_reinit_effect(instance, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
3809               weed_instance_unref(instance);
3810               continue;
3811 		// *INDENT-OFF*
3812             }}}
3813 	  // *INDENT-ON*
3814 
3815         orig_inst = instance;
3816 
3817 apply_audio_inst2:
3818 
3819         if (weed_plant_has_leaf(instance, WEED_LEAF_HOST_NEXT_INSTANCE)) {
3820           // chain any internal data pipelines for compound fx
3821           needs_reinit = pconx_chain_data_internal(instance);
3822           if (needs_reinit) {
3823             if ((filter_error = weed_reinit_effect(instance, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
3824               weed_instance_unref(instance);
3825               continue;
3826             }
3827           }
3828         }
3829 
3830         layers[0] = alayer;
3831         // will unref instance
3832         filter_error = weed_apply_audio_instance(instance, layers, 0, nchans, nsamps, arate, tc, NULL);
3833 
3834         if (filter_error == FILTER_SUCCESS && (instance = get_next_compound_inst(instance))) {
3835           goto apply_audio_inst2;
3836         }
3837         if (filter_error == FILTER_ERROR_NEEDS_REINIT) {
3838           // TODO...
3839         }
3840 
3841         weed_instance_unref(orig_inst);
3842 
3843         if (filter_error == FILTER_INFO_REINITED) update_widget_vis(NULL, i, key_modes[i]); // redraw our paramwindow
3844 #ifdef DEBUG_RTE
3845         if (filter_error != FILTER_SUCCESS) lives_printerr("Render error was %d\n", filter_error);
3846 #endif
3847         mainw->osc_block = FALSE;
3848 
3849         if (mainw->pconx && (filter_error == FILTER_SUCCESS || filter_error == FILTER_INFO_REINITED
3850                              || filter_error == FILTER_INFO_REDRAWN)) {
3851           pconx_chain_data_omc((instance = weed_instance_obtain(i, key_modes[i])), i, key_modes[i]);
3852           weed_instance_unref(instance);
3853 	  // *INDENT-OFF*
3854         }}}}
3855   // *INDENT-ON*
3856 }
3857 
3858 
has_audio_filters(lives_af_t af_type)3859 boolean has_audio_filters(lives_af_t af_type) {
3860   // do we have any active audio filters (excluding audio generators) ?
3861   // called from audio thread during playback
3862   weed_plant_t *filter;
3863   int idx;
3864   register int i;
3865 
3866   if (af_type == AF_TYPE_A && mainw->audio_frame_buffer) return TRUE;
3867 
3868   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
3869     if (rte_key_valid(i + 1, TRUE)) {
3870       if (rte_key_is_enabled(1 + i)) {
3871         idx = key_to_fx[i][key_modes[i]];
3872         filter = weed_filters[idx];
3873         if (has_audio_chans_in(filter, FALSE) && !has_video_chans_in(filter, FALSE) && !has_video_chans_out(filter, FALSE)) {
3874           if ((af_type == AF_TYPE_A && has_audio_chans_out(filter, FALSE)) || // check for analysers only
3875               (af_type == AF_TYPE_NONA && !has_audio_chans_out(filter, FALSE))) // check for non-analysers only
3876             continue;
3877           return TRUE;
3878 	  // *INDENT-OFF*
3879         }}}}
3880   // *INDENT-ON*
3881 
3882   return FALSE;
3883 }
3884 
3885 
has_video_filters(boolean analysers_only)3886 boolean has_video_filters(boolean analysers_only) {
3887   // do we have any active video filters (excluding generators) ?
3888   weed_plant_t *filter;
3889   int idx;
3890   register int i;
3891 
3892   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
3893     if (rte_key_valid(i + 1, TRUE)) {
3894       if (rte_key_is_enabled(1 + i)) {
3895         idx = key_to_fx[i][key_modes[i]];
3896         filter = weed_filters[idx];
3897         if (has_video_chans_in(filter, FALSE)) {
3898           if (analysers_only && has_video_chans_out(filter, FALSE)) continue;
3899           return TRUE;
3900 	  // *INDENT-OFF*
3901         }}}}
3902   // *INDENT-ON*
3903 
3904   return FALSE;
3905 }
3906 
3907 
3908 /////////////////////////////////////////////////////////////////////////
3909 
check_weed_plugin_info(weed_plant_t * plugin_info)3910 static int check_weed_plugin_info(weed_plant_t *plugin_info) {
3911   // verify the plugin_info returned from the plugin
3912   // TODO - print descriptive errors
3913   if (!weed_plant_has_leaf(plugin_info, WEED_LEAF_HOST_INFO)) return -1;
3914   if (!weed_plant_has_leaf(plugin_info, WEED_LEAF_VERSION)) return -2;
3915   if (!weed_plant_has_leaf(plugin_info, WEED_LEAF_FILTERS)) return -3;
3916   return weed_leaf_num_elements(plugin_info, WEED_LEAF_FILTERS);
3917 }
3918 
3919 
num_in_params(weed_plant_t * plant,boolean skip_hidden,boolean skip_internal)3920 int num_in_params(weed_plant_t *plant, boolean skip_hidden, boolean skip_internal) {
3921   weed_plant_t **params = NULL;
3922 
3923   weed_plant_t *param;
3924 
3925   int counted = 0;
3926   int num_params, i;
3927   boolean is_template = (WEED_PLANT_IS_FILTER_CLASS(plant));
3928 
3929 nip1:
3930 
3931   if (is_template) {
3932     if (!(params = weed_get_plantptr_array_counted(plant, WEED_LEAF_IN_PARAMETER_TEMPLATES, &num_params))) return 0;
3933   } else {
3934     if (!(params = weed_get_plantptr_array_counted(plant, WEED_LEAF_IN_PARAMETERS, &num_params))) goto nip1done;
3935   }
3936 
3937   if (!skip_hidden && !skip_internal) {
3938     counted += num_params;
3939     goto nip1done;
3940   }
3941 
3942   for (i = 0; i < num_params; i++) {
3943     if (skip_hidden && is_hidden_param(plant, i)) continue;
3944     param = params[i];
3945     if (skip_internal && weed_plant_has_leaf(param, WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
3946     counted++;
3947   }
3948 
3949 nip1done:
3950 
3951   lives_freep((void **)&params);
3952 
3953   // TODO: should be skip_internal or !skip_internal ?
3954   if (!is_template && skip_internal && (plant = get_next_compound_inst(plant)) != NULL) goto nip1;
3955 
3956   return counted;
3957 }
3958 
3959 
num_out_params(weed_plant_t * plant)3960 int num_out_params(weed_plant_t *plant) {
3961   int num_params, error;
3962   boolean is_template = (WEED_PLANT_IS_FILTER_CLASS(plant));
3963 
3964   if (is_template) {
3965     if (!weed_plant_has_leaf(plant, WEED_LEAF_OUT_PARAMETER_TEMPLATES) ||
3966         !weed_get_plantptr_value(plant, WEED_LEAF_OUT_PARAMETER_TEMPLATES, &error)) return 0;
3967     num_params = weed_leaf_num_elements(plant, WEED_LEAF_OUT_PARAMETER_TEMPLATES);
3968   } else {
3969     if (!weed_plant_has_leaf(plant, WEED_LEAF_OUT_PARAMETERS)) return 0;
3970     if (!weed_get_plantptr_value(plant, WEED_LEAF_OUT_PARAMETERS, &error)) return 0;
3971     num_params = weed_leaf_num_elements(plant, WEED_LEAF_OUT_PARAMETERS);
3972   }
3973   return num_params;
3974 }
3975 
has_usable_palette(weed_plant_t * chantmpl)3976 boolean has_usable_palette(weed_plant_t *chantmpl) {
3977   int palette = weed_get_int_value(chantmpl, WEED_LEAF_CURRENT_PALETTE, NULL);
3978   // currently only integer RGB palettes are usable
3979   if (palette == 5 || palette == 6) return FALSE;
3980   if (palette > 0 && palette <= 7) return TRUE;
3981   return FALSE;
3982 }
3983 
3984 
enabled_in_channels(weed_plant_t * plant,boolean count_repeats)3985 int enabled_in_channels(weed_plant_t *plant, boolean count_repeats) {
3986   // count number of non-disabled in channels (video and/or audio) for a filter or instance
3987 
3988   // NOTE: for instances, we do not count optional audio channels, even if they are enabled
3989 
3990   weed_plant_t **channels = NULL;
3991   weed_plant_t *filter;
3992   boolean is_template = WEED_PLANT_IS_FILTER_CLASS(plant);
3993   int enabled = 0;
3994   int num_channels;
3995 
3996   register int i;
3997 
3998   if (is_template) {
3999     filter = plant;
4000     if (!weed_plant_has_leaf(plant, WEED_LEAF_IN_CHANNEL_TEMPLATES)) return 0;
4001     channels = weed_get_plantptr_array_counted(plant, WEED_LEAF_IN_CHANNEL_TEMPLATES, &num_channels);
4002   } else {
4003     filter = weed_instance_get_filter(plant, TRUE);
4004     if (!weed_plant_has_leaf(plant, WEED_LEAF_IN_CHANNELS)) return 0;
4005     channels = weed_get_plantptr_array_counted(plant, WEED_LEAF_IN_CHANNELS, &num_channels);
4006   }
4007 
4008   for (i = 0; i < num_channels; i++) {
4009     if (!is_template) {
4010       weed_plant_t *ctmpl = weed_get_plantptr_value(channels[i], WEED_LEAF_TEMPLATE, NULL);
4011       if (weed_chantmpl_is_audio(ctmpl) == WEED_TRUE) {
4012         if (weed_chantmpl_is_optional(ctmpl)) continue;
4013       }
4014       if (weed_get_boolean_value(channels[i], WEED_LEAF_DISABLED, NULL) == WEED_FALSE) enabled++;
4015     } else {
4016       // skip alpha channels
4017       if (mainw->multitrack && !has_non_alpha_palette(channels[i], filter)) continue;
4018       if (weed_get_boolean_value(channels[i], WEED_LEAF_HOST_DISABLED, NULL) == WEED_FALSE) enabled++;
4019     }
4020 
4021     if (count_repeats) {
4022       // count repeated channels
4023       weed_plant_t *chantmpl;
4024       int repeats;
4025       if (is_template) chantmpl = channels[i];
4026       else chantmpl = weed_get_plantptr_value(channels[i], WEED_LEAF_TEMPLATE, NULL);
4027       if (weed_plant_has_leaf(channels[i], WEED_LEAF_MAX_REPEATS)) {
4028         if (weed_get_boolean_value(channels[i], WEED_LEAF_DISABLED, NULL) == WEED_TRUE &&
4029             !has_usable_palette(chantmpl)) continue; // channel was disabled because palette is unusable
4030         repeats = weed_get_int_value(channels[i], WEED_LEAF_MAX_REPEATS, NULL) - 1;
4031         if (repeats == -1) repeats = 1000000;
4032         enabled += repeats;
4033       }
4034     }
4035   }
4036 
4037   if (channels) lives_freep((void **)&channels);
4038 
4039   return enabled;
4040 }
4041 
4042 
enabled_out_channels(weed_plant_t * plant,boolean count_repeats)4043 int enabled_out_channels(weed_plant_t *plant, boolean count_repeats) {
4044   weed_plant_t **channels = NULL;
4045   int enabled = 0;
4046   int num_channels, i;
4047   boolean is_template = WEED_PLANT_IS_FILTER_CLASS(plant);
4048 
4049   if (is_template) {
4050     channels = weed_get_plantptr_array_counted(plant, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &num_channels);
4051   } else {
4052     channels = weed_get_plantptr_array_counted(plant, WEED_LEAF_OUT_CHANNELS, &num_channels);
4053   }
4054 
4055   for (i = 0; i < num_channels; i++) {
4056     if (!is_template) {
4057       if (weed_get_boolean_value(channels[i], WEED_LEAF_DISABLED, NULL) == WEED_FALSE) enabled++;
4058     } else {
4059       if (weed_get_boolean_value(channels[i], WEED_LEAF_HOST_DISABLED, NULL) == WEED_FALSE) enabled++;
4060     }
4061     if (count_repeats) {
4062       // count repeated channels
4063       weed_plant_t *chantmpl;
4064       int repeats;
4065       if (is_template) chantmpl = channels[i];
4066       else chantmpl = weed_get_plantptr_value(channels[i], WEED_LEAF_TEMPLATE, NULL);
4067       if (weed_plant_has_leaf(channels[i], WEED_LEAF_MAX_REPEATS)) {
4068         if (weed_get_boolean_value(channels[i], WEED_LEAF_DISABLED, NULL) == WEED_TRUE &&
4069             !has_usable_palette(chantmpl)) continue; // channel was disabled because palette is unusable
4070         repeats = weed_get_int_value(channels[i], WEED_LEAF_MAX_REPEATS, NULL) - 1;
4071         if (repeats == -1) repeats = 1000000;
4072         enabled += repeats;
4073 	// *INDENT-OFF*
4074       }}}
4075   // *INDENT-ON*
4076 
4077   if (channels) lives_freep((void **)&channels);
4078 
4079   return enabled;
4080 }
4081 
4082 
4083 /////////////////////////////////////////////////////////////////////
4084 
min_audio_chans(weed_plant_t * plant)4085 static int min_audio_chans(weed_plant_t *plant) {
4086   int *ents;
4087   int ne;
4088   int minas = 1, i;
4089   ents = weed_get_int_array_counted(plant, WEED_LEAF_AUDIO_CHANNELS, &ne);
4090   if (ne == 0) return 1;
4091   for (i = 0; i < ne; i++) {
4092     if (minas == 1 || (ents[i] > 0 && ents[i] < minas)) minas = ents[i];
4093     if (minas == 1) {
4094       lives_free(ents);
4095       return 1;
4096     }
4097   }
4098   lives_free(ents);
4099   return minas;
4100 }
4101 
4102 
check_for_lives(weed_plant_t * filter,int filter_idx)4103 static int check_for_lives(weed_plant_t *filter, int filter_idx) {
4104   // for LiVES, currently:
4105   // all filters must take 0, 1 or 2 mandatory/optional inputs and provide
4106   // 1 mandatory output (audio or video) or >1 optional (video only) outputs (for now)
4107 
4108   // or 1 or more mandatory alpha outputs (analysers)
4109 
4110   // filters can also have 1 mandatory input and no outputs and out parameters
4111   // (video analyzer)
4112 
4113   // they may have no outs provided they have out parameters (data sources or processors)
4114 
4115   // all channels used must support a limited range of palettes (for now)
4116 
4117   // filters can now have any number of mandatory in alphas, the effect will not be run unless the channels are filled
4118   // (by chaining to output of another fx)
4119   weed_plant_t **array = NULL;
4120 
4121   int chans_in_mand = 0; // number of mandatory channels
4122   int chans_in_opt_max = 0; // number of usable (by LiVES) optional channels
4123   int chans_out_mand = 0;
4124   int chans_out_opt_max = 0;
4125   int achans_in_mand = 0, achans_out_mand = 0;
4126 
4127   boolean is_audio = FALSE;
4128   boolean has_out_params = FALSE;
4129   boolean all_out_alpha = TRUE;
4130   boolean hidden = FALSE;
4131   boolean cvary = FALSE, pvary = FALSE;
4132 
4133   int flags = 0;
4134   int num_elements, i;
4135   int naudins = 0, naudouts = 0;
4136   int filter_achans = 0;
4137   int ctachans;
4138   //g_print("checking %s\n", weed_filter_get_name(filter));
4139   // TODO - check seed types
4140   if (!weed_plant_has_leaf(filter, WEED_LEAF_NAME)) return 1;
4141   if (!weed_plant_has_leaf(filter, WEED_LEAF_AUTHOR)) return 2;
4142   if (!weed_plant_has_leaf(filter, WEED_LEAF_VERSION)) return 3;
4143   if (!weed_plant_has_leaf(filter, WEED_LEAF_PROCESS_FUNC)) return 4;
4144 
4145   flags = weed_filter_get_flags(filter);
4146 
4147   // for now we will only load realtime effects
4148   if (flags & WEED_FILTER_NON_REALTIME) return 5;
4149   if (flags & WEED_FILTER_CHANNEL_LAYOUTS_MAY_VARY) cvary = TRUE;
4150   if (flags & WEED_FILTER_PALETTES_MAY_VARY) pvary = TRUE;
4151 
4152   // count number of mandatory and optional in_channels
4153   array = weed_filter_get_in_chantmpls(filter, &num_elements);
4154 
4155   for (i = 0; i < num_elements; i++) {
4156     if (!weed_plant_has_leaf(array[i], WEED_LEAF_NAME)) {
4157       lives_freep((void **)&array);
4158       return 6;
4159     }
4160 
4161     if (weed_chantmpl_is_audio(array[i]) == WEED_TRUE) {
4162       /// filter has audio channels
4163       if (filter_achans == 0) {
4164         filter_achans = min_audio_chans(filter);
4165       }
4166       // filter set nothing, or set a bad n umber but channels may vary
4167       // we're ok if the total number of mandatory non repeaters is <= 2
4168       if (!weed_chantmpl_is_optional(array[i])) {
4169         if (!cvary && (ctachans = min_audio_chans(array[i])) > 0)
4170           naudins += ctachans;
4171         else naudins += filter_achans;
4172         if (naudins > 2) {
4173           if (!prefs->vj_mode) {
4174             // currently we only handle mono and stereo audio filters
4175             char *filtname = weed_filter_get_name(filter);
4176             char *pkgstring = weed_filter_get_package_name(filter);
4177             char *msg, *pkstr;
4178             if (pkgstring) pkstr = lives_strdup_printf(" from package %s ", pkgstring);
4179             else pkstr = lives_strdup("");
4180             msg = lives_strdup_printf("Cannot use filter %s%s\n"
4181                                       "as it requires at least %d input audio channels\n",
4182                                       filtname, pkstr, naudins);
4183             lives_free(filtname); lives_free(pkstr); lives_free(pkgstring);
4184             LIVES_INFO(msg);
4185             lives_free(msg);
4186           }
4187           lives_freep((void **)&array);
4188           return 7;
4189         }
4190       }
4191       is_audio = TRUE;
4192     }
4193 
4194     if (!is_audio) {
4195       if (!weed_plant_has_leaf(filter, WEED_LEAF_PALETTE_LIST)) {
4196         if (!pvary || !weed_plant_has_leaf(array[i], WEED_LEAF_PALETTE_LIST)) {
4197           lives_freep((void **)&array);
4198           return 16;
4199         }
4200       } else {
4201         if (!weed_plant_has_leaf(array[i], WEED_LEAF_PALETTE_LIST))
4202           weed_leaf_copy(array[i], WEED_LEAF_PALETTE_LIST, filter, WEED_LEAF_PALETTE_LIST);
4203       }
4204     }
4205 
4206     if (weed_chantmpl_is_optional(array[i])) {
4207       // is optional
4208       chans_in_opt_max++;
4209       weed_set_boolean_value(array[i], WEED_LEAF_HOST_DISABLED, WEED_TRUE);
4210     } else {
4211       if (has_non_alpha_palette(array[i], filter)) {
4212         if (!is_audio) chans_in_mand++;
4213         else achans_in_mand++;
4214 	// *INDENT-OFF*
4215       }}}
4216   // *INDENT-ON*
4217 
4218   if (num_elements > 0) lives_freep((void **)&array);
4219   if (chans_in_mand > 2) {
4220     // currently we only handle mono and stereo audio filters
4221     if (!prefs->vj_mode) {
4222       char *filtname = weed_filter_get_name(filter);
4223       char *pkgstring = weed_filter_get_package_name(filter);
4224       char *msg, *pkstr;
4225       lives_freep((void **)&array);
4226       if (pkgstring) pkstr = lives_strdup_printf(" from package %s ", pkgstring);
4227       else pkstr = lives_strdup("");
4228       msg = lives_strdup_printf("Cannot use filter %s%s\n"
4229                                 "as it requires at least %d input video channels\n",
4230                                 filtname, pkstr, chans_in_mand);
4231       lives_free(filtname); lives_free(pkstr); lives_free(pkgstring);
4232       LIVES_INFO(msg);
4233       lives_free(msg);
4234     }
4235     return 8; // we dont handle mixers yet...
4236   }
4237   if (achans_in_mand > 0 && chans_in_mand > 0) return 13; // can't yet handle effects that need both audio and video
4238 
4239   // count number of mandatory and optional out_channels
4240   array = weed_filter_get_out_chantmpls(filter, &num_elements);
4241 
4242   for (i = 0; i < num_elements; i++) {
4243     if (!weed_plant_has_leaf(array[i], WEED_LEAF_NAME)) {
4244       lives_freep((void **)&array);
4245       return 6;
4246     }
4247 
4248     if (weed_chantmpl_is_audio(array[i]) == WEED_TRUE) {
4249       if (!weed_chantmpl_is_optional(array[i])) {
4250         if (!cvary && (ctachans = min_audio_chans(array[i])) > 0)
4251           naudouts += ctachans;
4252         else naudouts += filter_achans;
4253         if (naudouts > 2) {
4254           // currently we only handle mono and stereo audio filters
4255           lives_freep((void **)&array);
4256           return 7;
4257         }
4258       }
4259       is_audio = TRUE;
4260       if (naudins == 1 && naudouts == 2) {
4261         // converting mono to stereo cannot be done like that
4262         lives_freep((void **)&array);
4263         return 7;
4264       }
4265       is_audio = TRUE;
4266     }
4267 
4268     if (!is_audio) {
4269       if (!weed_plant_has_leaf(filter, WEED_LEAF_PALETTE_LIST)) {
4270         if (!pvary || !weed_plant_has_leaf(array[i], WEED_LEAF_PALETTE_LIST)) {
4271           lives_freep((void **)&array);
4272           return 16;
4273         }
4274       } else {
4275         if (!weed_plant_has_leaf(array[i], WEED_LEAF_PALETTE_LIST))
4276           weed_leaf_copy(array[i], WEED_LEAF_PALETTE_LIST, filter, WEED_LEAF_PALETTE_LIST);
4277       }
4278     }
4279 
4280     if (weed_chantmpl_is_optional(array[i])) {
4281       // is optional
4282       chans_out_opt_max++;
4283       weed_set_boolean_value(array[i], WEED_LEAF_HOST_DISABLED, WEED_TRUE);
4284     } else {
4285       // is mandatory
4286       if (!is_audio) {
4287         chans_out_mand++;
4288         if (has_non_alpha_palette(array[i], filter)) all_out_alpha = FALSE;
4289       } else achans_out_mand++;
4290     }
4291   }
4292 
4293   if (num_elements > 0) lives_freep((void **)&array);
4294   if (weed_plant_has_leaf(filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES)) has_out_params = TRUE;
4295 
4296   if ((chans_out_mand > 1 && !all_out_alpha) || ((chans_out_mand + chans_out_opt_max + achans_out_mand < 1)
4297       && (!has_out_params))) {
4298     return 11;
4299   }
4300   if (achans_out_mand > 1 || (achans_out_mand == 1 && chans_out_mand > 0)) return 14;
4301   if (achans_in_mand >= 1 && achans_out_mand == 0 && !(has_out_params)) return 15;
4302 
4303   weed_add_plant_flags(filter, WEED_FLAG_IMMUTABLE | WEED_FLAG_UNDELETABLE, "plugin_");
4304   if (weed_plant_has_leaf(filter, WEED_LEAF_GUI)) {
4305     weed_plant_t *gui = weed_get_plantptr_value(filter, WEED_LEAF_GUI, NULL);
4306     weed_add_plant_flags(gui, WEED_FLAG_IMMUTABLE | WEED_FLAG_UNDELETABLE, "plugin_");
4307     if (weed_get_boolean_value(gui, WEED_LEAF_HIDDEN, NULL) == WEED_TRUE) {
4308       hidden = TRUE;
4309     }
4310   }
4311 
4312   if (hidden || (flags & WEED_FILTER_IS_CONVERTER)) {
4313     if (is_audio) {
4314       weed_set_boolean_value(filter, WEED_LEAF_HOST_MENU_HIDE, WEED_TRUE);
4315       if (enabled_in_channels(filter, TRUE) >= 1000000) {
4316         // this is a candidate for audio volume
4317         lives_fx_candidate_t *cand = &mainw->fx_candidates[FX_CANDIDATE_AUDIO_VOL];
4318         cand->list = lives_list_append(cand->list, LIVES_INT_TO_POINTER(filter_idx));
4319         cand->delegate = 0;
4320       }
4321     } else {
4322       weed_set_boolean_value(filter, WEED_LEAF_HOST_MENU_HIDE, WEED_TRUE);
4323       if (chans_in_mand == 1 && chans_out_mand == 1) {
4324         if ((flags & WEED_FILTER_IS_CONVERTER) && (flags & WEED_FILTER_CHANNEL_SIZES_MAY_VARY)) {
4325           // this is a candidate for resize
4326           lives_fx_candidate_t *cand = &mainw->fx_candidates[FX_CANDIDATE_RESIZER];
4327           cand->list = lives_list_append(cand->list, LIVES_INT_TO_POINTER(filter_idx));
4328           cand->delegate = 0;
4329 	// *INDENT-OFF*
4330         }}}}
4331   // *INDENT-ON*
4332 
4333   return 0;
4334 }
4335 
4336 
4337 // Weed function overrides /////////////////
4338 
weed_plant_free_host(weed_plant_t * plant)4339 weed_error_t weed_plant_free_host(weed_plant_t *plant) {
4340   // delete even undeletable plants
4341   weed_error_t err;
4342   if (!plant) return WEED_ERROR_NOSUCH_PLANT;
4343   err = _weed_plant_free(plant);
4344   if (err == WEED_ERROR_UNDELETABLE) {
4345     weed_clear_plant_flags(plant, WEED_FLAG_UNDELETABLE, NULL);
4346     return _weed_plant_free(plant);
4347   }
4348   return err;
4349 }
4350 
4351 
4352 /* weed_plant_t *weed_plant_new_host(int type) { */
4353 /*   return _weed_plant_new(type); */
4354 /* } */
4355 
4356 
weed_leaf_delete_host(weed_plant_t * plant,const char * key)4357 weed_error_t weed_leaf_delete_host(weed_plant_t *plant, const char *key) {
4358   // delete even undeletable leaves
4359   weed_error_t err;
4360   if (!plant) return WEED_ERROR_NOSUCH_PLANT;
4361   err = _weed_leaf_delete(plant, key);
4362   if (err == WEED_ERROR_UNDELETABLE) {
4363     weed_leaf_clear_flagbits(plant, key, WEED_FLAG_UNDELETABLE);
4364     err = _weed_leaf_delete(plant, key);
4365     if (err != WEED_SUCCESS) abort();
4366   }
4367   return err;
4368 }
4369 
4370 
weed_leaf_set_host(weed_plant_t * plant,const char * key,uint32_t seed_type,weed_size_t num_elems,void * values)4371 weed_error_t weed_leaf_set_host(weed_plant_t *plant, const char *key, uint32_t seed_type,
4372                                 weed_size_t num_elems, void *values) {
4373   // change even immutable leaves
4374   weed_error_t err;
4375 
4376   if (!plant) return WEED_ERROR_NOSUCH_PLANT;
4377 
4378   do {
4379     err = _weed_leaf_set(plant, key, seed_type, num_elems, values);
4380   } while (err == WEED_ERROR_CONCURRENCY);
4381   if (err == WEED_ERROR_IMMUTABLE) {
4382     int32_t flags = weed_leaf_get_flags(plant, key);
4383     flags ^= WEED_FLAG_IMMUTABLE;
4384     weed_leaf_set_flags(plant, key, flags);
4385     err = _weed_leaf_set(plant, key, seed_type, num_elems, values);
4386     flags |= WEED_FLAG_IMMUTABLE;
4387     weed_leaf_set_flags(plant, key, flags);
4388   }
4389   return err;
4390 }
4391 
4392 
4393 /* weed_error_t weed_leaf_set_plugin(weed_plant_t *plant, const char *key, uint32_t seed_type, weed_size_t num_elems, void *values) { */
4394 /*   fprintf(stderr, "pl setting %s\n", key); */
4395 /*   return _weed_leaf_set(plant, key, seed_type, num_elems, values); */
4396 /* } */
4397 
upd_statsplant(const char * key)4398 static void upd_statsplant(const char *key) {
4399   int freq;
4400   if (mainw->is_exiting) return;
4401   if (!statsplant) statsplant = weed_plant_new(0);
4402   _weed_leaf_get(statsplant, key, WEED_SEED_INT, &freq);
4403   _weed_leaf_set(statsplant, key, WEED_SEED_INT, 1, &freq);
4404 }
4405 
4406 
4407 // memory profiling for plugins
4408 
lives_monitor_malloc(size_t size)4409 void *lives_monitor_malloc(size_t size) {
4410   void *p = malloc(size);
4411   fprintf(stderr, "plugin mallocing %ld bytes, got ptr %p\n", size, p);
4412   if (size == 1024) break_me("monitor_malloc");
4413   return NULL;
4414 }
4415 
4416 
lives_monitor_free(void * p)4417 void lives_monitor_free(void *p) {
4418   //fprintf(stderr, "plugin freeing ptr ptr %p\n", p);
4419 }
4420 
4421 
weed_leaf_set_monitor(weed_plant_t * plant,const char * key,uint32_t seed_type,weed_size_t num_elems,void * values)4422 weed_error_t weed_leaf_set_monitor(weed_plant_t *plant, const char *key, uint32_t seed_type, weed_size_t num_elems,
4423                                    void *values) {
4424   weed_error_t err;
4425   err = _weed_leaf_set(plant, key, seed_type, num_elems, values);
4426   g_print("PL setting %s in type %d\n", key, weed_plant_get_type(plant));
4427 
4428   if (WEED_PLANT_IS_GUI(plant) && !strcmp(key, WEED_LEAF_FLAGS)) g_print("Err was %d\n", err);
4429   return err;
4430 }
4431 
4432 
weed_leaf_get_monitor(weed_plant_t * plant,const char * key,int32_t idx,void * value)4433 weed_error_t weed_leaf_get_monitor(weed_plant_t *plant, const char *key, int32_t idx, void *value) {
4434   // plugins can be monitored for example
4435   weed_error_t err = _weed_leaf_get(plant, key, idx, value);
4436   // and simulating bugs...
4437   /* if (!strcmp(key, WEED_LEAF_WIDTH) && value) { */
4438   /*   int *ww = (int *)value; */
4439   /*   *ww -= 100; */
4440   /* } */
4441   upd_statsplant(key);
4442   return err;
4443 }
4444 
4445 #if 0
4446 static void **dta = NULL;
4447 static int *rws = NULL;
4448 static int ht = 0;
4449 static int npls = 0;
4450 
4451 void weed_mem_chkreg(void **data, int *rs, int height, int nplanes) {
4452   dta = data;
4453   rws = rs;
4454   ht = height;
4455   npls = npanes
4456 }
4457 
4458 
4459 void *lives_memcpy_monitor(void *d, const void *s, size_t sz) {
4460   if (dta) {
4461     for (int i = 0; i < npls; i++) {
4462       void *end = dta[i] + rws[i] * ht;
4463       if (d > end && d - end < 8) fprintf(stderr, "pl overwrite\n");
4464     }
4465     return lives_memcpy(d, s, sz);
4466   }
4467 }
4468 #endif
4469 
weed_leaf_num_elements_monitor(weed_plant_t * plant,const char * key)4470 weed_size_t weed_leaf_num_elements_monitor(weed_plant_t *plant, const char *key) {
4471   upd_statsplant(key);
4472   return _weed_leaf_num_elements(plant, key);
4473 }
4474 
4475 
show_weed_stats(weed_plant_t * statsplant)4476 void show_weed_stats(weed_plant_t *statsplant) {
4477   LiVESList *freq = NULL, *sorted = NULL, *list;
4478   char **leaves;
4479   int val, i, added = 0, min, lmin = 0;
4480   weed_size_t nleaves;
4481 
4482   if (!statsplant) return;
4483   leaves = weed_plant_list_leaves(statsplant, &nleaves);
4484   /// sort in descending order
4485   for (i = 0; i < nleaves; i++) {
4486     int f = weed_get_int_value(statsplant, leaves[i], NULL);
4487     freq = lives_list_prepend(freq, LIVES_INT_TO_POINTER(f));
4488     //g_print("added %s with freq %d\n", leaves[i], f);
4489   }
4490   while (added < nleaves) {
4491     min = LIVES_MAXINT32;
4492     for (list = freq; list; list = list->next) {
4493       val = LIVES_POINTER_TO_INT(list->data);
4494       if (val < min && val > lmin) min = val;
4495     }
4496     //g_print("next min was %d\n", min);
4497     i = nleaves - 1;
4498     for (list = freq; list; list = list->next) {
4499       val = LIVES_POINTER_TO_INT(list->data);
4500       if (val == min) {
4501         //g_print("prep. %d %s\n", i, leaves[i]);
4502         sorted = lives_list_prepend(sorted, LIVES_INT_TO_POINTER(i));
4503         if (++added == nleaves) break;
4504       }
4505       i--;
4506     }
4507     if (min == lmin) break;
4508     lmin = min;
4509   }
4510   for (list = sorted; list; list = list->next) {
4511     val = LIVES_POINTER_TO_INT(list->data);
4512     g_print("STATS: %s : %d\n", leaves[val], weed_get_int_value(statsplant, leaves[val], NULL));
4513     free(leaves[val]);
4514   }
4515   free(leaves);
4516   lives_list_free(freq);
4517   lives_list_free(sorted);
4518   weed_plant_free(statsplant);
4519 }
4520 
4521 
host_info_cb(weed_plant_t * xhost_info,void * data)4522 weed_plant_t *host_info_cb(weed_plant_t *xhost_info, void *data) {
4523   // if the plugin called weed_boostrap during its weed_setup() as we requested, then we will end up here
4524   // from weed_bootstrap()
4525   int id = LIVES_POINTER_TO_INT(data);
4526 
4527   int lib_weed_version = 0;
4528   int lib_filter_version = 0;
4529 
4530   if (id == 100) {
4531     // the openGL plugin
4532     our_plugin_id = 100;
4533     fxname = NULL;
4534   }
4535 
4536   suspect = TRUE;
4537 
4538   ncbcalls++;
4539   if (id != 100 && ncbcalls > 1) {
4540     return NULL;
4541   }
4542 
4543   if (!xhost_info) {
4544     return NULL;
4545   }
4546 
4547   expected_hi = xhost_info;
4548 
4549   if (id == our_plugin_id) {
4550     // GOOD !
4551     // plugin called weed_bootstrap as requested
4552     //g_print("plugin called weed_bootstrap, and we assigned it ID %d\n", our_plugin_id);
4553     weed_set_int_value(xhost_info, WEED_LEAF_HOST_IDENTIFIER, id);
4554     weed_set_boolean_value(xhost_info, WEED_LEAF_HOST_SUSPICIOUS, WEED_FALSE);
4555     suspect = FALSE;
4556   } else {
4557     weed_set_int_value(xhost_info, WEED_LEAF_HOST_IDENTIFIER, 0);
4558     weed_set_boolean_value(xhost_info, WEED_LEAF_HOST_SUSPICIOUS, WEED_TRUE);
4559   }
4560 
4561   // we can do cool stuff here !
4562 
4563   if (!suspect) {
4564     // let's check the versions the library is using...
4565     if (weed_plant_has_leaf(xhost_info, WEED_LEAF_WEED_ABI_VERSION)) {
4566       lib_weed_version = weed_get_int_value(xhost_info, WEED_LEAF_WEED_ABI_VERSION, NULL);
4567 
4568       // this allows us to load plugins compiled with older versions of the lib
4569       // we must not set it any higher than it is though
4570       // this is redundant really, since we already checked before calling weed_init()
4571       if (lib_weed_version > weed_abi_version)
4572         weed_set_int_value(xhost_info, WEED_LEAF_WEED_ABI_VERSION, weed_abi_version);
4573     }
4574     if (weed_plant_has_leaf(xhost_info, WEED_LEAF_FILTER_API_VERSION)) {
4575       lib_filter_version = weed_get_int_value(xhost_info, WEED_LEAF_FILTER_API_VERSION, NULL);
4576       if (lib_filter_version > WEED_FILTER_API_VERSION)
4577         weed_set_int_value(xhost_info, WEED_LEAF_FILTER_API_VERSION, WEED_FILTER_API_VERSION);
4578     }
4579   }
4580 
4581   if (weed_plant_has_leaf(xhost_info, WEED_LEAF_PLUGIN_INFO)) {
4582     // let's check the versions the plugin supports
4583     weed_plant_t *plugin_info = weed_get_plantptr_value(xhost_info, WEED_LEAF_PLUGIN_INFO, NULL);
4584     int pl_max_weed_abi, pl_max_filter_api;
4585     if (plugin_info) {
4586       expected_pi = plugin_info;
4587       // we don't need to bother with min versions, the lib will check for us
4588       /* if (weed_plant_has_leaf(plugin_info, WEED_LEAF_MIN_WEED_ABI_VERSION)) { */
4589       /* 	pl_min_weed_api = weed_get_int_value(plugin_info, WEED_LEAF_MIN_WEED_ABI_VERSION, NULL); */
4590       /* } */
4591       if (weed_plant_has_leaf(plugin_info, WEED_LEAF_MAX_WEED_ABI_VERSION)) {
4592         pl_max_weed_abi = weed_get_int_value(plugin_info, WEED_LEAF_MAX_WEED_ABI_VERSION, NULL);
4593         // we can support all versions back to 110
4594         if (pl_max_weed_abi < weed_abi_version && pl_max_weed_abi >= 110) {
4595           weed_set_int_value(xhost_info, WEED_LEAF_WEED_ABI_VERSION, pl_max_weed_abi);
4596         }
4597       }
4598       // we don't need to bother with min versions, the lib will check for us
4599       /* if (weed_plant_has_leaf(plugin_info, WEED_LEAF_MIN_WEED_API_VERSION)) { */
4600       /* 	pl_min_filter_api = weed_get_int_value(plugin_info, WEED_LEAF_MIN_FILTER_API_VERSION, NULL); */
4601       /* } */
4602       if (weed_plant_has_leaf(plugin_info, WEED_LEAF_MAX_WEED_API_VERSION)) {
4603         pl_max_filter_api = weed_get_int_value(plugin_info, WEED_LEAF_MAX_FILTER_API_VERSION, NULL);
4604         // we can support all versions back to 110
4605         if (pl_max_filter_api < WEED_FILTER_API_VERSION && pl_max_filter_api >= 110) {
4606           weed_set_int_value(xhost_info, WEED_LEAF_FILTER_API_VERSION, pl_max_filter_api);
4607 	  // *INDENT-OFF*
4608         }}}}
4609   // *INDENT-ON*
4610 
4611   //  fprintf(stderr, "API versions %d %d / %d %d : %d %d\n", lib_weed_version, lib_filter_version,
4612   //pl_min_weed_api, pl_max_weed_api, pl_min_filter_api, pl_max_filter_api);
4613 
4614 #ifndef USE_STD_MEMFUNCS
4615   // let's override some plugin functions...
4616   if (id == 100) {
4617     weed_set_funcptr_value(xhost_info, WEED_LEAF_MALLOC_FUNC, (weed_funcptr_t)_ext_malloc);
4618     weed_set_funcptr_value(xhost_info, WEED_LEAF_FREE_FUNC, (weed_funcptr_t)_ext_free);
4619     weed_set_funcptr_value(xhost_info, WEED_LEAF_REALLOC_FUNC, (weed_funcptr_t)_ext_realloc);
4620     weed_set_funcptr_value(xhost_info, WEED_LEAF_CALLOC_FUNC, (weed_funcptr_t)_ext_calloc);
4621   } else {
4622     weed_set_funcptr_value(xhost_info, WEED_LEAF_MALLOC_FUNC, (weed_funcptr_t)lives_malloc);
4623     weed_set_funcptr_value(xhost_info, WEED_LEAF_FREE_FUNC, (weed_funcptr_t)lives_free);
4624     weed_set_funcptr_value(xhost_info, WEED_LEAF_REALLOC_FUNC, (weed_funcptr_t)lives_realloc);
4625     weed_set_funcptr_value(xhost_info, WEED_LEAF_CALLOC_FUNC, (weed_funcptr_t)lives_calloc);
4626   }
4627   //weed_set_funcptr_value(xhost_info, WEED_LEAF_MEMCPY_FUNC, (weed_funcptr_t)lives_memcpy_monitor);
4628   weed_set_funcptr_value(xhost_info, WEED_LEAF_MEMCPY_FUNC, (weed_funcptr_t)lives_memcpy);
4629   weed_set_funcptr_value(xhost_info, WEED_LEAF_MEMSET_FUNC, (weed_funcptr_t)lives_memset);
4630   weed_set_funcptr_value(xhost_info, WEED_LEAF_MEMMOVE_FUNC, (weed_funcptr_t)lives_memmove);
4631 #endif
4632   //weed_set_funcptr_value(xhost_info, WEED_LEAF_MALLOC_FUNC, (weed_funcptr_t)monitor_malloc);
4633   //weed_set_funcptr_value(xhost_info, WEED_LEAF_FREE_FUNC, (weed_funcptr_t)monitor_free);
4634 
4635   // since we redefined weed_leaf_set, weed_leaf_delete and weed_plant_free for ourselves,
4636   // we need to reset the plugin versions, since it will inherit ours by default when calling setup_func()
4637 
4638   weed_set_funcptr_value(xhost_info, WEED_LEAF_SET_FUNC, (weed_funcptr_t)_weed_leaf_set);
4639   weed_set_funcptr_value(xhost_info, WEED_LEAF_DELETE_FUNC, (weed_funcptr_t)_weed_leaf_delete);
4640   weed_set_funcptr_value(xhost_info, WEED_PLANT_FREE_FUNC, (weed_funcptr_t)_weed_plant_free);
4641   //weed_set_funcptr_value(xhost_info, WEED_LEAF_NUM_ELEMENTS_FUNC, (weed_funcptr_t)_weed_leaf_num_elements);
4642 
4643   weed_set_string_value(xhost_info, WEED_LEAF_HOST_NAME, "LiVES");
4644   weed_set_string_value(xhost_info, WEED_LEAF_HOST_VERSION, LiVES_VERSION);
4645 
4646   weed_set_string_value(xhost_info, WEED_LEAF_LAYOUT_SCHEMES, "rfx");
4647 
4648   weed_set_int_value(xhost_info, WEED_LEAF_FLAGS, WEED_HOST_SUPPORTS_LINEAR_GAMMA
4649                      | WEED_HOST_SUPPORTS_PREMULTIPLIED_ALPHA);
4650 
4651   if (fxname && !strcmp(fxname, "projectM")) {
4652     //  weed_set_funcptr_value(xhost_info, WEED_LEAF_SET_FUNC, (weed_funcptr_t)weed_leaf_set_monitor);
4653     //weed_set_int_value(xhost_info, WEED_LEAF_VERBOSITY, WEED_VERBOSITY_DEBUG);
4654   } else
4655     weed_set_int_value(xhost_info, WEED_LEAF_VERBOSITY, WEED_VERBOSITY_WARN);
4656 
4657   update_host_info(xhost_info);
4658   return xhost_info;
4659 }
4660 
4661 
4662 ///////////////////////////////////////////////////////////////////////////////////////
4663 
gen_hashnames(int i,int j)4664 static void gen_hashnames(int i, int j) {
4665   hashnames[i][0].string = 	NULL;
4666   hashnames[i][0].string = 	make_weed_hashname(j, TRUE, FALSE, 0, FALSE);
4667   hashnames[i][0].hash =  	lives_string_hash(hashnames[i][0].string);
4668   hashnames[i][1].string = 	NULL;
4669   hashnames[i][1].string = 	make_weed_hashname(j, TRUE, TRUE, 0, FALSE); // full dupes
4670   hashnames[i][1].hash =  	lives_string_hash(hashnames[i][1].string);
4671   hashnames[i][2].string = 	NULL;
4672   hashnames[i][2].string = 	make_weed_hashname(j, FALSE, FALSE, 0, FALSE); // partial pdupes
4673   hashnames[i][2].hash =  	lives_string_hash(hashnames[i][2].string);
4674   hashnames[i][3].string = 	NULL;
4675   hashnames[i][3].string =	make_weed_hashname(j, FALSE, TRUE, 0, FALSE);
4676   hashnames[i][3].hash =  	lives_string_hash(hashnames[i][3].string);
4677 
4678   hashnames[i][4].string = 	NULL;
4679   hashnames[i][4].string = 	make_weed_hashname(j, TRUE, FALSE, 0, TRUE);
4680   hashnames[i][4].hash =  	lives_string_hash(hashnames[i][4].string);
4681   hashnames[i][5].string = 	NULL;
4682   hashnames[i][5].string = 	make_weed_hashname(j, TRUE, TRUE, 0, TRUE); // full dupes
4683   hashnames[i][5].hash =  	lives_string_hash(hashnames[i][5].string);
4684   hashnames[i][6].string = 	NULL;
4685   hashnames[i][6].string = 	make_weed_hashname(j, FALSE, FALSE, 0, TRUE); // partial pdupes
4686   hashnames[i][6].hash =  	lives_string_hash(hashnames[i][6].string);
4687   hashnames[i][7].string = 	NULL;
4688   hashnames[i][7].string =	make_weed_hashname(j, FALSE, TRUE, 0, TRUE);
4689   hashnames[i][7].hash =  	lives_string_hash(hashnames[i][7].string);
4690 }
4691 
4692 
load_weed_plugin(char * plugin_name,char * plugin_path,char * dir)4693 static void load_weed_plugin(char *plugin_name, char *plugin_path, char *dir) {
4694 #if defined TEST_ISOL && defined LM_ID_NEWLM
4695   static Lmid_t lmid = LM_ID_NEWLM;
4696   static boolean have_lmid = FALSE;
4697   Lmid_t new_lmid;
4698 #endif
4699   weed_setup_f setup_fn;
4700   weed_plant_t *plugin_info = NULL, **filters = NULL, *filter = NULL;
4701   weed_plant_t *host_info;
4702   void *handle;
4703   int dlflags = RTLD_NOW | RTLD_LOCAL;
4704   int reason, idx = num_weed_filters;
4705   int filters_in_plugin, fnum, j;
4706 
4707   char cwd[PATH_MAX];
4708 
4709   // filters which can cause a segfault
4710   const char *frei0r_blacklist[] = {"Timeout indicator", NULL};
4711   const char *ladspa_blacklist[] = {"Mag's Notch Filter", "Identity (Control)", "Signal Branch (IC)",
4712                                     "Signal Product (ICIC)", "Signal Difference (ICMC)",
4713                                     "Signal Sum (ICIC)", "Signal Ratio (NCDC)",
4714                                     NULL
4715                                    };
4716 
4717   char *pwd, *tmp, *msg, *filtname;
4718   char *filter_name = NULL, *package_name = NULL;
4719   boolean blacklisted;
4720   boolean none_valid = TRUE;
4721 
4722   register int i;
4723 
4724 #ifdef RTLD_DEEPBIND
4725   dlflags |= RTLD_DEEPBIND;
4726 #endif
4727 
4728   pwd = getcwd(cwd, PATH_MAX);
4729   THREADVAR(chdir_failed) = FALSE;
4730 
4731   // walk list and create fx structures
4732   //#define DEBUG_WEED
4733 #ifdef DEBUG_WEED
4734   lives_printerr("Checking plugin %s\n", plugin_path);
4735 #endif
4736 
4737 #if defined TEST_ISOL && defined LM_ID_NEWLM
4738   if (!have_lmid) {
4739     handle = dlmopen(LM_ID_NEWLM, plugin_path, dlflags);
4740   } else {
4741     handle = dlmopen(lmid, plugin_path, dlflags);
4742   }
4743   if (handle) {
4744     dlerror(); // clear existing errors
4745     if (!have_lmid) {
4746       have_lmid = TRUE;
4747       dlinfo(handle, RTLD_DI_LMID, &new_lmid);
4748       dlerror(); // clear existing errors
4749       lmid = new_lmid;
4750     }
4751   }
4752 #else
4753   if ((handle = dlopen(plugin_path, dlflags))) {
4754     dlerror(); // clear existing errors
4755   }
4756 #endif
4757   else {
4758     msg = lives_strdup_printf(_("Unable to load plugin %s\nError was: %s\n"), plugin_path, dlerror());
4759     LIVES_WARN(msg);
4760     lives_free(msg);
4761     return;
4762   }
4763   if ((setup_fn = (weed_setup_f)dlsym(handle, "weed_setup")) == NULL) {
4764     msg = lives_strdup_printf(_("Error: plugin %s has no weed_setup() function.\n"), plugin_path);
4765     LIVES_ERROR(msg);
4766     lives_free(msg);
4767     return;
4768   }
4769 
4770   // here we call the plugin's setup_fn, passing in our bootstrap function
4771   // the plugin will call our bootstrap function to get the correct versions of the core weed functions
4772   // and bootstrap itself
4773 
4774   // if we use the plugin, we must not free the plugin_info, since the plugin has a reference to this
4775 
4776   // chdir to plugin dir, in case it needs to load data
4777 
4778   // set a callback so we can adjust plugin functions (provided the plugin calls weed_bootstrap as we request)
4779   host_info = NULL;
4780   our_plugin_id = 0;
4781   do {
4782     our_plugin_id = (int)((lives_random() * lives_random()) & 0xFFFF);
4783   } while (our_plugin_id == 0);
4784 
4785   weed_set_host_info_callback(host_info_cb, LIVES_INT_TO_POINTER(our_plugin_id));
4786 
4787   lives_chdir(dir, TRUE);
4788   suspect = TRUE;
4789   expected_hi = expected_pi = NULL;
4790   ncbcalls = 0;
4791   fxname = strip_ext(plugin_name);
4792 
4793   plugin_info = (*setup_fn)(weed_bootstrap);
4794 
4795   if (!plugin_info || (filters_in_plugin = check_weed_plugin_info(plugin_info)) < 1) {
4796     msg = lives_strdup_printf(_("No usable filters found in plugin:\n%s\n"), plugin_path);
4797     LIVES_INFO(msg);
4798     lives_free(msg);
4799     if (plugin_info) weed_plant_free(plugin_info);
4800     dlclose(handle);
4801     lives_chdir(pwd, FALSE);
4802     lives_freep((void **)&fxname);
4803     return;
4804   }
4805 
4806   lives_freep((void **)&fxname);
4807 
4808   if (expected_pi && plugin_info != expected_pi) suspect = TRUE;
4809   if (weed_plant_has_leaf(plugin_info, WEED_LEAF_HOST_INFO)) {
4810     host_info = weed_get_plantptr_value(plugin_info, WEED_LEAF_HOST_INFO, NULL);
4811     if (!host_info || host_info != expected_hi) {
4812       // filter switched the host_info...very naughty
4813       if (host_info) weed_plant_free(host_info);
4814       msg = lives_strdup_printf("Badly behaved plugin (%s)\n", plugin_path);
4815       LIVES_WARN(msg);
4816       lives_free(msg);
4817       if (plugin_info) weed_plant_free(plugin_info);
4818       dlclose(handle);
4819       lives_chdir(pwd, FALSE);
4820       return;
4821     }
4822     if (weed_plant_has_leaf(host_info, WEED_LEAF_PLUGIN_INFO)) {
4823       weed_plant_t *pi = weed_get_plantptr_value(host_info, WEED_LEAF_PLUGIN_INFO, NULL);
4824       if (!pi || pi != plugin_info) {
4825         // filter switched the plugin_info...very naughty
4826         msg = lives_strdup_printf("Badly behaved plugin - %s %p %p %p %p\n",
4827                                   plugin_path, pi, plugin_info, host_info, expected_hi);
4828         LIVES_WARN(msg);
4829         lives_free(msg);
4830         if (pi) weed_plant_free(pi);
4831         if (host_info) weed_plant_free(host_info);
4832         if (plugin_info) weed_plant_free(plugin_info);
4833         dlclose(handle);
4834         lives_chdir(pwd, FALSE);
4835         return;
4836       }
4837     } else {
4838       suspect = TRUE;
4839       weed_set_plantptr_value(host_info, WEED_LEAF_PLUGIN_INFO, plugin_info);
4840     }
4841   } else {
4842     if (suspect || !expected_hi) {
4843       msg = lives_strdup_printf("Badly behaved plugin: %s\n", plugin_path);
4844       LIVES_WARN(msg);
4845       lives_free(msg);
4846       if (host_info) weed_plant_free(host_info);
4847       if (plugin_info) weed_plant_free(plugin_info);
4848       dlclose(handle);
4849       lives_chdir(pwd, FALSE);
4850       return;
4851     }
4852     host_info = expected_hi;
4853     suspect = TRUE;
4854     weed_set_plantptr_value(plugin_info, WEED_LEAF_HOST_INFO, expected_hi);
4855   }
4856   if (!suspect) {
4857     weed_set_boolean_value(plugin_info, WEED_LEAF_HOST_SUSPICIOUS, WEED_FALSE);
4858     weed_set_boolean_value(host_info, WEED_LEAF_HOST_SUSPICIOUS, WEED_FALSE);
4859   } else {
4860     weed_set_boolean_value(plugin_info, WEED_LEAF_HOST_SUSPICIOUS, WEED_TRUE);
4861     weed_set_boolean_value(host_info, WEED_LEAF_HOST_SUSPICIOUS, WEED_TRUE);
4862   }
4863   weed_set_voidptr_value(plugin_info, WEED_LEAF_HOST_HANDLE, handle);
4864   weed_set_string_value(plugin_info, WEED_LEAF_HOST_PLUGIN_NAME, plugin_name); // for hashname
4865   weed_set_string_value(plugin_info, WEED_LEAF_HOST_PLUGIN_PATH, dir);
4866   weed_add_plant_flags(plugin_info, WEED_FLAG_IMMUTABLE | WEED_FLAG_UNDELETABLE, "plugin_");
4867 
4868   filters = weed_get_plantptr_array(plugin_info, WEED_LEAF_FILTERS, NULL);
4869 
4870   if (weed_plant_has_leaf(plugin_info, WEED_LEAF_PACKAGE_NAME)) {
4871     package_name = lives_strdup_printf("%s: ", (tmp = weed_get_string_value(plugin_info,
4872                                        WEED_LEAF_PACKAGE_NAME, NULL)));
4873     lives_free(tmp);
4874   } else package_name = lives_strdup("");
4875 
4876   for (fnum = 0; fnum < filters_in_plugin; fnum++) {
4877     filter = filters[fnum];
4878     blacklisted = FALSE;
4879 
4880     if (!filter) {
4881       msg = lives_strdup_printf(_("Plugin %s returned an empty filter !"), plugin_path);
4882       LIVES_WARN(msg);
4883       lives_free(msg);
4884       continue;
4885     }
4886 
4887     filtname = weed_filter_get_name(filter);
4888     blacklisted = FALSE;
4889 
4890     if (!strcmp(package_name, "Frei0r: ")) {
4891       for (i = 0; frei0r_blacklist[i]; i++) {
4892         if (!strcmp(filtname, frei0r_blacklist[i])) {
4893           blacklisted = TRUE;
4894           break;
4895 	  // *INDENT-OFF*
4896 	}}}
4897     // *INDENT-ON*
4898 
4899     if (!strcmp(package_name, "LADSPA: ")) {
4900       for (i = 0; ladspa_blacklist[i]; i++) {
4901         if (!strcmp(filtname, ladspa_blacklist[i])) {
4902           blacklisted = TRUE;
4903           break;
4904 	  // *INDENT-OFF*
4905 	}}}
4906     // *INDENT-ON*
4907 
4908     if (blacklisted) {
4909       if (!prefs->vj_mode) {
4910         msg = lives_strdup_printf(_("%sskipping blacklisted filter %s\n"), package_name, filtname);
4911         fprintf(stderr, "%s", msg);
4912         lives_free(msg);
4913       }
4914       continue;
4915     }
4916 
4917     filter_name = lives_strdup_printf("%s%s:", package_name, filtname);
4918     lives_free(filtname);
4919 
4920     // add value returned in host_info_cb
4921     if (host_info) weed_set_plantptr_value(filter, WEED_LEAF_HOST_INFO, host_info);
4922 
4923     if (!(reason = check_for_lives(filter, idx))) {
4924       boolean dup = FALSE;
4925       none_valid = FALSE;
4926       num_weed_filters++;
4927       weed_filters = (weed_plant_t **)lives_realloc(weed_filters, num_weed_filters * sizeof(weed_plant_t *));
4928       weed_filters[idx] = filter;
4929 
4930       hashnames = (lives_hashjoint *)lives_realloc(hashnames, num_weed_filters * sizeof(lives_hashjoint));
4931       gen_hashnames(idx, idx);
4932 
4933       for (i = 0; i < idx; i++) {
4934         if (hashnames[idx][1].hash == hashnames[i][1].hash
4935             && !lives_utf8_strcasecmp(hashnames[idx][1].string, hashnames[i][1].string)) {
4936           // skip dups
4937           if (!prefs->vj_mode) {
4938             msg = lives_strdup_printf(_("Found duplicate plugin %s"), hashnames[idx][1].string);
4939             LIVES_INFO(msg);
4940             lives_free(msg);
4941           }
4942           for (j = 0; j < NHASH_TYPES; j ++) {
4943             lives_freep((void **)&hashnames[idx][j].string);
4944           }
4945           num_weed_filters--;
4946           weed_filters = (weed_plant_t **)lives_realloc(weed_filters, num_weed_filters * sizeof(weed_plant_t *));
4947           hashnames = (lives_hashjoint *)lives_realloc(hashnames, num_weed_filters * sizeof(lives_hashjoint));
4948           lives_freep((void **)&filter_name);
4949           dup = TRUE;
4950           break;
4951         }
4952 
4953         if (hashnames[i][2].hash == hashnames[idx][2].hash
4954             && !lives_utf8_strcasecmp(hashnames[i][2].string, hashnames[idx][2].string)) {
4955           //g_print("partial dupe: %s and %s\n",phashnames[phashes-1],phashnames[i-oidx-1]);
4956           // found a partial match: author and/or version differ
4957           // hide oldder version from menus
4958           if (weed_get_int_value(filter, WEED_LEAF_VERSION, NULL) <
4959               weed_get_int_value(weed_filters[i], WEED_LEAF_VERSION, NULL)) {
4960             weed_set_boolean_value(filter, WEED_LEAF_HOST_MENU_HIDE, WEED_TRUE);
4961           } else {
4962             weed_set_boolean_value(weed_filters[i], WEED_LEAF_HOST_MENU_HIDE, WEED_TRUE);
4963           }
4964           break;
4965         }
4966       }
4967 
4968       if (dup) continue;
4969       idx++;
4970       if (prefs->show_splash) {
4971         msg = lives_strdup_printf((tmp = _("Loaded filter %s in plugin %s")), filter_name, plugin_name);
4972         lives_free(tmp);
4973         splash_msg(msg, SPLASH_LEVEL_LOAD_RTE);
4974         lives_free(msg);
4975       }
4976     } else {
4977 #ifdef DEBUG_WEED
4978       lives_printerr("Unsuitable filter \"%s\" in plugin \"%s\", reason code %d\n",
4979                      filter_name, plugin_name, reason);
4980 #endif
4981     }
4982     lives_freep((void **)&filter_name);
4983   }
4984 
4985   if (none_valid && plugin_info) {
4986     host_info = weed_get_plantptr_value(plugin_info, WEED_LEAF_HOST_INFO, NULL);
4987     if (host_info) weed_plant_free(host_info);
4988     weed_plant_free(plugin_info);
4989   }
4990 
4991   lives_freep((void **)&filters);
4992   lives_free(package_name);
4993   if (THREADVAR(chdir_failed)) {
4994     char *dirs = (_("Some plugin directories"));
4995     do_chdir_failed_error(dirs);
4996     lives_free(dirs);
4997   }
4998 
4999   lives_chdir(pwd, FALSE);
5000 
5001   // TODO - add any rendered effects to fx submenu
5002 }
5003 
5004 
make_fx_defs_menu(int num_weed_compounds)5005 static void make_fx_defs_menu(int num_weed_compounds) {
5006   weed_plant_t *filter;
5007 
5008   LiVESWidget *menuitem, *menu = mainw->rte_defs;
5009   LiVESWidget *pkg_menu;
5010   LiVESWidget *pkg_submenu = NULL;
5011 
5012   LiVESList *menu_list = NULL;
5013   LiVESList *pkg_menu_list = NULL;
5014   LiVESList *compound_list = NULL;
5015 
5016   char *string, *filter_type, *filter_name;
5017   char *pkg = NULL, *pkgstring;
5018   boolean hidden;
5019   register int i;
5020 
5021   weed_fx_sorted_list = NULL;
5022 
5023   // menu entries for vj/set defs
5024   for (i = 0; i < num_weed_filters; i++) {
5025     filter = weed_filters[i];
5026 
5027     if (weed_filter_hints_unstable(filter) && !prefs->show_dev_opts && !prefs->unstable_fx) continue;
5028     filter_name = weed_filter_idx_get_name(i, FALSE, FALSE);
5029 
5030     // skip hidden filters
5031     if (weed_get_boolean_value(filter, WEED_LEAF_HOST_MENU_HIDE, NULL) == WEED_TRUE ||
5032         (num_in_params(filter, TRUE, TRUE) == 0 && !(!has_video_chans_in(filter, FALSE) && has_video_chans_out(filter, FALSE))) ||
5033         (enabled_in_channels(filter, TRUE) > 2 && enabled_out_channels(filter, TRUE) > 0))
5034       hidden = TRUE;
5035     else hidden = FALSE;
5036 
5037     pkgstring = weed_filter_get_package_name(filter);
5038 
5039     if (pkgstring) {
5040       // new package
5041       if (pkg && strcmp(pkg, pkgstring)) {
5042         pkg_menu_list = add_sorted_list_to_menu(LIVES_MENU(menu), pkg_menu_list);
5043         lives_list_free(pkg_menu_list);
5044         pkg_menu_list = NULL;
5045         menu = mainw->rte_defs;
5046         lives_freep((void **)&pkg);
5047       }
5048 
5049       if (!pkg) {
5050         pkg = pkgstring;
5051         /* TRANSLATORS: example " - LADSPA plugins -" */
5052         pkgstring = lives_strdup_printf(_(" - %s plugins -"), pkg);
5053         // create new submenu
5054 
5055         widget_opts.mnemonic_label = FALSE;
5056         pkg_menu = lives_standard_menu_item_new_with_label(pkgstring);
5057         widget_opts.mnemonic_label = TRUE;
5058         lives_container_add(LIVES_CONTAINER(mainw->rte_defs), pkg_menu);
5059 
5060         pkg_submenu = lives_menu_new();
5061         lives_menu_item_set_submenu(LIVES_MENU_ITEM(pkg_menu), pkg_submenu);
5062 
5063         if (palette->style & STYLE_1) {
5064           lives_widget_set_bg_color(pkg_submenu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
5065           lives_widget_set_fg_color(pkg_submenu, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
5066         }
5067 
5068         lives_widget_show(pkg_menu);
5069         lives_widget_show(pkg_submenu);
5070         lives_free(pkgstring);
5071 
5072         // add to submenu
5073         menu = pkg_submenu;
5074       }
5075     } else {
5076       pkg_menu_list = add_sorted_list_to_menu(LIVES_MENU(menu), pkg_menu_list);
5077       lives_list_free(pkg_menu_list);
5078       pkg_menu_list = NULL;
5079       lives_freep((void **)&pkg);
5080       menu = mainw->rte_defs;
5081     }
5082     filter_type = weed_filter_get_type(filter, TRUE, FALSE);
5083     string = lives_strdup_printf("%s (%s)", filter_name, filter_type);
5084 
5085     widget_opts.mnemonic_label = FALSE;
5086     menuitem = lives_standard_menu_item_new_with_label(string);
5087     widget_opts.mnemonic_label = TRUE;
5088     lives_free(string);
5089     lives_free(filter_type);
5090 
5091     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), HIDDEN_KEY, LIVES_INT_TO_POINTER((int)hidden));
5092     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), SECLIST_KEY, &weed_fx_sorted_list);
5093     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(menuitem), SECLIST_VAL_KEY, LIVES_INT_TO_POINTER(i));
5094 
5095     if (pkg) pkg_menu_list = lives_list_prepend(pkg_menu_list, (livespointer)menuitem);
5096     else {
5097       if (i >= num_weed_filters - num_weed_compounds) compound_list = lives_list_prepend(compound_list, (livespointer)menuitem);
5098       else menu_list = lives_list_prepend(menu_list, (livespointer)menuitem);
5099     }
5100 
5101     lives_signal_connect(LIVES_GUI_OBJECT(menuitem), LIVES_WIDGET_ACTIVATE_SIGNAL,
5102                          LIVES_GUI_CALLBACK(rte_set_defs_activate), LIVES_INT_TO_POINTER(i));
5103 
5104     lives_freep((void **)&filter_name);
5105   }
5106 
5107   if (pkg) {
5108     pkg_menu_list = add_sorted_list_to_menu(LIVES_MENU(menu), pkg_menu_list);
5109     lives_list_free(pkg_menu_list);
5110     lives_free(pkg);
5111   }
5112   menu_list = add_sorted_list_to_menu(LIVES_MENU(mainw->rte_defs), menu_list);
5113   lives_list_free(menu_list);
5114   compound_list = add_sorted_list_to_menu(LIVES_MENU(mainw->rte_defs), compound_list);
5115   lives_list_free(compound_list);
5116 }
5117 
5118 
weed_load_all(void)5119 void weed_load_all(void) {
5120   // get list of plugins from directory and create our fx
5121   LiVESList *weed_plugin_list, *weed_plugin_sublist;
5122   char **dirs;
5123   char *subdir_path, *subdir_name, *plugin_path, *plugin_name;
5124   int max_modes = prefs->max_modes_per_key;
5125   int numdirs, ncompounds;
5126   int i, j;
5127 
5128   num_weed_filters = 0;
5129 
5130   weed_filters = NULL;
5131   hashnames = NULL;
5132 
5133   threaded_dialog_spin(0.);
5134 
5135 #ifdef DEBUG_WEED
5136   lives_printerr("In weed init\n");
5137 #endif
5138 
5139   fg_gen_to_start = fg_generator_key = fg_generator_clip = fg_generator_mode = -1;
5140   bg_gen_to_start = bg_generator_key = bg_generator_mode = -1;
5141 
5142   for (i = 0; i < FX_KEYS_MAX; i++) {
5143     if (i == FX_KEYS_MAX_VIRTUAL) max_modes = 1;
5144 
5145     key_to_instance[i] = (weed_plant_t **)lives_calloc(max_modes, sizeof(weed_plant_t *));
5146 
5147     key_to_instance_copy[i] = (weed_plant_t **)lives_calloc(1, sizeof(weed_plant_t *));
5148 
5149     key_to_fx[i] = (int *)lives_calloc(max_modes, sizint);
5150 
5151     if (i < FX_KEYS_MAX_VIRTUAL)
5152       // *INDENT-OFF*
5153       key_defaults[i] = (weed_plant_t ***)lives_calloc(max_modes, sizeof(weed_plant_t **));
5154     // *INDENT-ON*
5155 
5156     key_modes[i] = 0; // current active mode of each key
5157     filter_map[i] = NULL; // maps effects in order of application for multitrack rendering
5158     for (j = 0; j < max_modes; j++) key_to_fx[i][j] = -1;
5159   }
5160 
5161   filter_map[FX_KEYS_MAX + 1] = NULL;
5162 
5163   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
5164     pchains[i] = NULL;
5165     init_events[i] = NULL;
5166   }
5167 
5168   next_free_key = FX_KEYS_MAX_VIRTUAL;
5169 
5170   threaded_dialog_spin(0.);
5171 
5172   // first we parse the weed_plugin_path
5173 #ifndef IS_MINGW
5174   numdirs = get_token_count(prefs->weed_plugin_path, ':');
5175   dirs = lives_strsplit(prefs->weed_plugin_path, ":", numdirs);
5176 #else
5177   numdirs = get_token_count(prefs->weed_plugin_path, ';');
5178   dirs = lives_strsplit(prefs->weed_plugin_path, ";", numdirs);
5179 #endif
5180   threaded_dialog_spin(0.);
5181 
5182   for (i = 0; i < numdirs; i++) {
5183     // get list of all files
5184     LiVESList *list = weed_plugin_list = get_plugin_list(PLUGIN_EFFECTS_WEED, TRUE, dirs[i], NULL);
5185 
5186     // parse twice, first we get the plugins, then 1 level of subdirs
5187     while (list) {
5188       LiVESList *listnext = list->next;
5189       threaded_dialog_spin(0.);
5190       plugin_name = (char *)list->data;
5191       if (!lives_strncmp(plugin_name + lives_strlen(plugin_name) - strlen(DLL_NAME) - 1, "." DLL_NAME, strlen(DLL_NAME) + 1)) {
5192         plugin_path = lives_build_filename(dirs[i], plugin_name, NULL);
5193         load_weed_plugin(plugin_name, plugin_path, dirs[i]);
5194         lives_freep((void **)&plugin_name);
5195         lives_free(plugin_path);
5196         list->data = NULL;
5197         if (list->prev) list->prev->next = listnext;
5198         else weed_plugin_list = listnext;
5199         if (listnext) listnext->prev = list->prev;
5200         list->prev = list->next = NULL;
5201         lives_list_free(list);
5202       }
5203       list = listnext;
5204       threaded_dialog_spin(0.);
5205     }
5206 
5207     // get 1 level of subdirs
5208     for (list = weed_plugin_list; list; list = list->next) {
5209       threaded_dialog_spin(0.);
5210       subdir_name = (char *)list->data;
5211       subdir_path = lives_build_filename(dirs[i], subdir_name, NULL);
5212       if (!lives_file_test(subdir_path, LIVES_FILE_TEST_IS_DIR) || !strcmp(subdir_name, "icons") || !strcmp(subdir_name, "data")) {
5213         lives_free(subdir_path);
5214         continue;
5215       }
5216 
5217       for (LiVESList *list2 = weed_plugin_sublist = get_plugin_list(PLUGIN_EFFECTS_WEED, TRUE, subdir_path, DLL_NAME);
5218            list2; list2 = list2 ->next) {
5219         plugin_name = (char *)list2->data;
5220         plugin_path = lives_build_filename(subdir_path, plugin_name, NULL);
5221         load_weed_plugin(plugin_name, plugin_path, subdir_path);
5222         lives_free(plugin_path);
5223       }
5224       lives_list_free_all(&weed_plugin_sublist);
5225       lives_freep((void **)&subdir_path);
5226       threaded_dialog_spin(0.);
5227     }
5228     lives_list_free_all(&weed_plugin_list);
5229   }
5230 
5231   lives_strfreev(dirs);
5232 
5233   d_print(_("Successfully loaded %d Weed filters\n"), num_weed_filters);
5234 
5235   threaded_dialog_spin(0.);
5236   ncompounds = load_compound_fx();
5237   threaded_dialog_spin(0.);
5238   make_fx_defs_menu(ncompounds);
5239   threaded_dialog_spin(0.);
5240   weed_fx_sorted_list = lives_list_reverse(weed_fx_sorted_list);
5241   fx_inited = TRUE;
5242 }
5243 
5244 
weed_plant_free_if_not_in_list(weed_plant_t * plant,LiVESList ** freed_ptrs)5245 static void weed_plant_free_if_not_in_list(weed_plant_t *plant, LiVESList **freed_ptrs) {
5246   /// avoid duplicate frees when unloading fx plugins
5247   if (freed_ptrs) {
5248     LiVESList *list = *freed_ptrs;
5249     while (list) {
5250       if (plant == (weed_plant_t *)list->data) return;
5251       list = list->next;
5252     }
5253     *freed_ptrs = lives_list_prepend(*freed_ptrs, (livespointer)plant);
5254   }
5255   weed_plant_free(plant);
5256 }
5257 
5258 
weed_filter_free(weed_plant_t * filter,LiVESList ** freed_ptrs)5259 static void weed_filter_free(weed_plant_t *filter, LiVESList **freed_ptrs) {
5260   int nitems, i;
5261   weed_plant_t **plants, *gui;
5262   boolean is_compound = FALSE;
5263 
5264   if (num_compound_fx(filter) > 1) is_compound = TRUE;
5265 
5266   // free in_param_templates
5267   plants = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, &nitems);
5268   if (nitems > 0) {
5269     for (i = 0; i < nitems; i++) {
5270       if (weed_plant_has_leaf(plants[i], WEED_LEAF_GUI)) {
5271         gui = (weed_get_plantptr_value(plants[i], WEED_LEAF_GUI, NULL));
5272         weed_plant_free_if_not_in_list(gui, freed_ptrs);
5273       }
5274       weed_plant_free_if_not_in_list(plants[i], freed_ptrs);
5275     }
5276     lives_freep((void **)&plants);
5277   }
5278 
5279   if (is_compound) {
5280     weed_plant_free(filter);
5281     return;
5282   }
5283 
5284   // free in_channel_templates
5285   if (weed_plant_has_leaf(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES)) {
5286     plants = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, &nitems);
5287     if (nitems > 0) {
5288       for (i = 0; i < nitems; i++) weed_plant_free_if_not_in_list(plants[i], freed_ptrs);
5289       lives_freep((void **)&plants);
5290     }
5291   }
5292 
5293   // free out_channel_templates
5294   if (weed_plant_has_leaf(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES)) {
5295     plants = weed_get_plantptr_array_counted(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &nitems);
5296     if (nitems > 0) {
5297       for (i = 0; i < nitems; i++) weed_plant_free_if_not_in_list(plants[i], freed_ptrs);
5298       lives_freep((void **)&plants);
5299     }
5300   }
5301 
5302   // free out_param_templates
5303   plants = weed_get_plantptr_array_counted(filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES, &nitems);
5304   if (nitems > 0) {
5305     threaded_dialog_spin(0.);
5306     for (i = 0; i < nitems; i++) {
5307       if (weed_plant_has_leaf(plants[i], WEED_LEAF_GUI))
5308         weed_plant_free(weed_get_plantptr_value(plants[i], WEED_LEAF_GUI, NULL));
5309       weed_plant_free_if_not_in_list(plants[i], freed_ptrs);
5310     }
5311     lives_freep((void **)&plants);
5312   }
5313 
5314   // free gui
5315   if (weed_plant_has_leaf(filter, WEED_LEAF_GUI))
5316     weed_plant_free_if_not_in_list(weed_get_plantptr_value(filter, WEED_LEAF_GUI, NULL), freed_ptrs);
5317 
5318   // free filter
5319   weed_plant_free(filter);
5320 }
5321 
5322 
create_compound_filter(char * plugin_name,int nfilts,int * filts)5323 static weed_plant_t *create_compound_filter(char *plugin_name, int nfilts, int *filts) {
5324   weed_plant_t *filter = weed_plant_new(WEED_PLANT_FILTER_CLASS), *xfilter, *gui;
5325   weed_plant_t **in_params = NULL, **out_params = NULL, **params;
5326   weed_plant_t **in_chans, **out_chans;
5327 
5328   char *tmp;
5329 
5330   double tgfps = -1., tfps;
5331 
5332   int count, xcount, error, nvals;
5333   int txparam = -1, tparam, txvolm = -1, tvolm;
5334 
5335   register int i, j, x;
5336 
5337   weed_set_int_array(filter, WEED_LEAF_HOST_FILTER_LIST, nfilts, filts);
5338 
5339   // create parameter templates - concatenate all sub filters
5340   count = xcount = 0;
5341 
5342   for (i = 0; i < nfilts; i++) {
5343     xfilter = weed_filters[filts[i]];
5344 
5345     if (weed_plant_has_leaf(xfilter, WEED_LEAF_PREFERRED_FPS)) {
5346       tfps = weed_get_double_value(xfilter, WEED_LEAF_PREFERRED_FPS, &error);
5347       if (tgfps == -1.) tgfps = tfps;
5348       else if (tgfps != tfps) {
5349         if (!prefs->vj_mode) {
5350           d_print((tmp = lives_strdup_printf(_("Invalid compound effect %s - has conflicting target_fps\n"), plugin_name)));
5351           LIVES_ERROR(tmp);
5352           lives_free(tmp);
5353         }
5354         return NULL;
5355       }
5356     }
5357 
5358     // in_parameter templates are copy-by-value, since we may set a different default/host_default value, and a different gui
5359 
5360     // everything else - filter classes, out_param templates and channels are copy by ref.
5361 
5362     if (weed_plant_has_leaf(xfilter, WEED_LEAF_IN_PARAMETER_TEMPLATES)) {
5363       tparam = get_transition_param(xfilter, FALSE);
5364 
5365       // TODO *** - ignore transtion params if they are connected to themselves
5366 
5367       if (tparam != -1) {
5368         if (txparam != -1) {
5369           if (!prefs->vj_mode) {
5370             d_print((tmp = lives_strdup_printf(_("Invalid compound effect %s - has multiple transition parameters\n"),
5371                                                plugin_name)));
5372             LIVES_ERROR(tmp);
5373             lives_free(tmp);
5374           }
5375           return NULL;
5376         }
5377         txparam = tparam;
5378       }
5379 
5380       tvolm = get_master_vol_param(xfilter, FALSE);
5381 
5382       // TODO *** - ignore master vol params if they are connected to themselves
5383 
5384       if (tvolm != -1) {
5385         if (txvolm != -1) {
5386           if (!prefs->vj_mode) {
5387             d_print((tmp = lives_strdup_printf(_("Invalid compound effect %s - has multiple master volume parameters\n"),
5388                                                plugin_name)));
5389             LIVES_ERROR(tmp);
5390             lives_free(tmp);
5391           }
5392           return NULL;
5393         }
5394         txvolm = tvolm;
5395       }
5396 
5397       params = weed_get_plantptr_array_counted(xfilter, WEED_LEAF_IN_PARAMETER_TEMPLATES, &nvals);
5398       count += nvals;
5399 
5400       in_params = (weed_plant_t **)lives_realloc(in_params, count * sizeof(weed_plant_t *));
5401       x = 0;
5402 
5403       for (j = xcount; j < count; j++) {
5404         in_params[j] = weed_plant_copy(params[x]);
5405         gui = weed_get_plantptr_value(params[x], WEED_LEAF_GUI, &error);
5406         if (gui) weed_set_plantptr_value(in_params[j], WEED_LEAF_GUI, weed_plant_copy(gui));
5407 
5408         if (x == tparam) {
5409           weed_set_boolean_value(in_params[j], WEED_LEAF_IS_TRANSITION, WEED_TRUE);
5410         } else if (weed_plant_has_leaf(in_params[j], WEED_LEAF_IS_TRANSITION))
5411           weed_leaf_delete(in_params[j], WEED_LEAF_IS_TRANSITION);
5412 
5413         if (x == tvolm) {
5414           weed_set_boolean_value(in_params[j], WEED_LEAF_IS_VOLUME_MASTER, WEED_TRUE);
5415         } else if (weed_plant_has_leaf(in_params[j], WEED_LEAF_IS_VOLUME_MASTER))
5416           weed_leaf_delete(in_params[j], WEED_LEAF_IS_VOLUME_MASTER);
5417 
5418         x++;
5419       }
5420       lives_free(params);
5421       xcount = count;
5422     }
5423   }
5424 
5425   if (tgfps != -1.) weed_set_double_value(filter, WEED_LEAF_PREFERRED_FPS, tgfps);
5426 
5427   if (count > 0) {
5428     weed_set_plantptr_array(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, count, in_params);
5429     lives_free(in_params);
5430   }
5431 
5432   count = xcount = 0;
5433 
5434   for (i = 0; i < nfilts; i++) {
5435     xfilter = weed_filters[filts[i]];
5436     if (weed_plant_has_leaf(xfilter, WEED_LEAF_OUT_PARAMETER_TEMPLATES)) {
5437       params = weed_get_plantptr_array_counted(xfilter, WEED_LEAF_OUT_PARAMETER_TEMPLATES, &nvals);
5438       count += nvals;
5439 
5440       out_params = (weed_plant_t **)lives_realloc(out_params, count * sizeof(weed_plant_t *));
5441       x = 0;
5442 
5443       for (j = xcount; j < count; j++) {
5444         out_params[j] = params[x++];
5445       }
5446       if (params) lives_free(params);
5447       xcount = count;
5448     }
5449   }
5450 
5451   if (count > 0) {
5452     weed_set_plantptr_array(filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES, count, out_params);
5453     lives_free(out_params);
5454   }
5455 
5456   // use in channels from first filter
5457 
5458   xfilter = weed_filters[filts[0]];
5459   if (weed_plant_has_leaf(xfilter, WEED_LEAF_IN_CHANNEL_TEMPLATES)) {
5460     in_chans = weed_get_plantptr_array_counted(xfilter, WEED_LEAF_IN_CHANNEL_TEMPLATES, &count);
5461     weed_set_plantptr_array(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, count, in_chans);
5462     lives_free(in_chans);
5463   }
5464 
5465   // use out channels from last filter
5466 
5467   xfilter = weed_filters[filts[nfilts - 1]];
5468   if (weed_plant_has_leaf(xfilter, WEED_LEAF_OUT_CHANNEL_TEMPLATES)) {
5469     out_chans = weed_get_plantptr_array_counted(xfilter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &count);
5470     weed_set_plantptr_array(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, count, out_chans);
5471     lives_free(out_chans);
5472   }
5473 
5474   return filter;
5475 }
5476 
5477 
load_compound_plugin(char * plugin_name,char * plugin_path)5478 static void load_compound_plugin(char *plugin_name, char *plugin_path) {
5479   FILE *cpdfile;
5480 
5481   weed_plant_t *filter = NULL, *ptmpl, *iptmpl, *xfilter;
5482   char buff[16384];
5483   char **array, **svals;
5484   char *author = NULL, *tmp, *key;
5485   int *filters = NULL, *ivals;
5486   double *dvals;
5487   int xvals[4];
5488 
5489   boolean ok = TRUE, autoscale;
5490   int stage = 0, nfilts = 0, fnum, line = 0, version = 0;
5491   int qvals = 1, ntok, xfilt, xfilt2;
5492   int xconx = 0;
5493   int nparams, nchans, pnum, pnum2, xpnum2, cnum, cnum2, ptype, pptype, pcspace, pflags;
5494   int error;
5495 
5496   register int i;
5497 
5498   if ((cpdfile = fopen(plugin_path, "r"))) {
5499     while (fgets(buff, 16384, cpdfile)) {
5500       line++;
5501       lives_strstrip(buff);
5502       if (!(*buff)) {
5503         if (++stage == 5) break;
5504         if (stage == 2) {
5505           if (nfilts < 2) {
5506             if (!prefs->vj_mode) {
5507               d_print((tmp = lives_strdup_printf(_("Invalid compound effect %s - must have >1 sub filters\n"), plugin_name)));
5508               LIVES_ERROR(tmp);
5509               lives_free(tmp);
5510             }
5511             ok = FALSE;
5512             break;
5513           }
5514           filter = create_compound_filter(plugin_name, nfilts, filters);
5515           if (!filter) break;
5516         }
5517         continue;
5518       }
5519       switch (stage) {
5520       case 0:
5521         if (line == 1) author = lives_strdup(buff);
5522         if (line == 2) version = atoi(buff);
5523         break;
5524       case 1:
5525         // add filters
5526         fnum = weed_get_idx_for_hashname(buff, TRUE);
5527         if (fnum == -1) {
5528           if (!prefs->vj_mode) {
5529             d_print((tmp = lives_strdup_printf(_("Invalid effect %s found in compound effect %s, line %d\n"),
5530                                                buff, plugin_name, line)));
5531             LIVES_INFO(tmp);
5532             lives_free(tmp);
5533           }
5534           ok = FALSE;
5535           break;
5536         }
5537         filters = (int *)lives_realloc(filters, ++nfilts * sizeof(int));
5538         filters[nfilts - 1] = fnum;
5539         break;
5540       case 2:
5541         // override defaults: format is i|p|d0|d1|...|dn
5542         // NOTE: for obvious reasons, | may not be used inside string defaults
5543         ntok = get_token_count(buff, '|');
5544 
5545         if (ntok < 2) {
5546           if (!prefs->vj_mode) {
5547             d_print((tmp = lives_strdup_printf(_("Invalid default found in compound effect %s, line %d\n"), plugin_name, line)));
5548             LIVES_ERROR(tmp);
5549             lives_free(tmp);
5550           }
5551           ok = FALSE;
5552           break;
5553         }
5554 
5555         array = lives_strsplit(buff, "|", ntok);
5556 
5557         xfilt = atoi(array[0]); // sub filter number
5558         if (xfilt < 0 || xfilt >= nfilts) {
5559           if (!prefs->vj_mode) {
5560             d_print((tmp = lives_strdup_printf(_("Invalid filter %d for defaults found in compound effect %s, line %d\n"),
5561                                                xfilt, plugin_name, line)));
5562             LIVES_ERROR(tmp);
5563             lives_free(tmp);
5564           }
5565           ok = FALSE;
5566           lives_strfreev(array);
5567           break;
5568         }
5569         xfilter = get_weed_filter(filters[xfilt]);
5570 
5571         nparams = num_in_params(xfilter, FALSE, FALSE);
5572 
5573         pnum = atoi(array[1]);
5574 
5575         if (pnum >= nparams) {
5576           if (!prefs->vj_mode) {
5577             d_print((tmp = lives_strdup_printf(_("Invalid param %d for defaults found in compound effect %s, line %d\n"),
5578                                                pnum, plugin_name, line)));
5579             LIVES_ERROR(tmp);
5580             lives_free(tmp);
5581           }
5582           ok = FALSE;
5583           lives_strfreev(array);
5584           break;
5585         }
5586 
5587         // get pnum for compound filter
5588         for (i = 0; i < xfilt; i++) pnum += num_in_params(get_weed_filter(filters[i]), FALSE, FALSE);
5589 
5590         ptmpl = weed_filter_in_paramtmpl(filter, pnum, FALSE);
5591 
5592         ptype = weed_leaf_seed_type(ptmpl, WEED_LEAF_DEFAULT);
5593         pflags = weed_get_int_value(ptmpl, WEED_LEAF_FLAGS, &error);
5594         pptype = weed_paramtmpl_get_type(ptmpl);
5595 
5596         if (pptype == WEED_PARAM_COLOR) {
5597           pcspace = weed_get_int_value(ptmpl, WEED_LEAF_COLORSPACE, &error);
5598           qvals = 3;
5599           if (pcspace == WEED_COLORSPACE_RGBA) qvals = 4;
5600         }
5601 
5602         ntok -= 2;
5603 
5604         if ((ntok != weed_leaf_num_elements(ptmpl, WEED_LEAF_DEFAULT) && !(pflags & WEED_PARAMETER_VARIABLE_SIZE)) ||
5605             ntok % qvals != 0) {
5606           if (!prefs->vj_mode) {
5607             d_print((tmp = lives_strdup_printf(_("Invalid number of values for defaults found in compound effect %s, line %d\n"),
5608                                                plugin_name, line)));
5609             LIVES_ERROR(tmp);
5610             lives_free(tmp);
5611           }
5612           ok = FALSE;
5613           lives_strfreev(array);
5614           break;
5615         }
5616 
5617         // TODO - for INT and DOUBLE, check if default is within min/max bounds
5618 
5619         switch (ptype) {
5620         case WEED_SEED_INT:
5621           ivals = (int *)lives_malloc(ntok * sizint);
5622           for (i = 0; i < ntok; i++) {
5623             ivals[i] = atoi(array[i + 2]);
5624           }
5625           weed_set_int_array(ptmpl, WEED_LEAF_DEFAULT, ntok, ivals);
5626           lives_free(ivals);
5627           break;
5628         case WEED_SEED_DOUBLE:
5629           dvals = (double *)lives_malloc(ntok * sizdbl);
5630           for (i = 0; i < ntok; i++) {
5631             dvals[i] = strtod(array[i + 2], NULL);
5632           }
5633           weed_set_double_array(ptmpl, WEED_LEAF_DEFAULT, ntok, dvals);
5634           lives_free(dvals);
5635           break;
5636         case WEED_SEED_BOOLEAN:
5637           ivals = (int *)lives_malloc(ntok * sizint);
5638           for (i = 0; i < ntok; i++) {
5639             ivals[i] = atoi(array[i + 2]);
5640 
5641             if (ivals[i] != WEED_TRUE && ivals[i] != WEED_FALSE) {
5642               lives_free(ivals);
5643               if (!prefs->vj_mode) {
5644                 d_print((tmp = lives_strdup_printf(_("Invalid non-boolean value for defaults found in compound effect %s, "
5645                                                      "line %d\n"), pnum, plugin_name, line)));
5646                 LIVES_ERROR(tmp);
5647                 lives_free(tmp);
5648               }
5649               ok = FALSE;
5650               lives_strfreev(array);
5651               break;
5652             }
5653 
5654           }
5655           weed_set_boolean_array(ptmpl, WEED_LEAF_DEFAULT, ntok, ivals);
5656           lives_free(ivals);
5657           break;
5658         default: // string
5659           svals = (char **)lives_malloc(ntok * sizeof(char *));
5660           for (i = 0; i < ntok; i++) {
5661             svals[i] = lives_strdup(array[i + 2]);
5662           }
5663           weed_set_string_array(ptmpl, WEED_LEAF_DEFAULT, ntok, svals);
5664           for (i = 0; i < ntok; i++) {
5665             lives_free(svals[i]);
5666           }
5667           lives_free(svals);
5668           break;
5669         }
5670         lives_strfreev(array);
5671         break;
5672       case 3:
5673         // link params: format is f0|p0|autoscale|f1|p1
5674 
5675         ntok = get_token_count(buff, '|');
5676 
5677         if (ntok != 5) {
5678           if (!prefs->vj_mode) {
5679             d_print((tmp = lives_strdup_printf(_("Invalid param link found in compound effect %s, line %d\n"),
5680                                                plugin_name, line)));
5681             LIVES_ERROR(tmp);
5682             lives_free(tmp);
5683           }
5684           ok = FALSE;
5685           break;
5686         }
5687 
5688         array = lives_strsplit(buff, "|", ntok);
5689 
5690         xfilt = atoi(array[0]); // sub filter number
5691         if (xfilt < -1 || xfilt >= nfilts) {
5692           if (!prefs->vj_mode) {
5693             d_print((tmp = lives_strdup_printf(_("Invalid out filter %d for link params found in compound effect %s, line %d\n"),
5694                                                xfilt, plugin_name, line)));
5695             LIVES_ERROR(tmp);
5696             lives_free(tmp);
5697           }
5698           ok = FALSE;
5699           lives_strfreev(array);
5700           break;
5701         }
5702 
5703         pnum = atoi(array[1]);
5704 
5705         if (xfilt > -1) {
5706           xfilter = get_weed_filter(filters[xfilt]);
5707 
5708           if (weed_plant_has_leaf(xfilter, WEED_LEAF_OUT_PARAMETER_TEMPLATES))
5709             nparams = weed_leaf_num_elements(xfilter, WEED_LEAF_OUT_PARAMETER_TEMPLATES);
5710           else nparams = 0;
5711 
5712           if (pnum >= nparams) {
5713             if (!prefs->vj_mode) {
5714               d_print((tmp = lives_strdup_printf(_("Invalid out param %d for link params found in compound effect %s, line %d\n"),
5715                                                  pnum, plugin_name, line)));
5716               LIVES_ERROR(tmp);
5717               lives_free(tmp);
5718             }
5719             ok = FALSE;
5720             lives_strfreev(array);
5721             break;
5722           }
5723         }
5724 
5725         autoscale = atoi(array[2]);
5726 
5727         if (autoscale != WEED_TRUE && autoscale != WEED_FALSE) {
5728           if (!prefs->vj_mode) {
5729             d_print((tmp = lives_strdup_printf(_("Invalid non-boolean value for autoscale found in compound effect %s, "
5730                                                  "line %d\n"), pnum, plugin_name, line)));
5731             LIVES_ERROR(tmp);
5732             lives_free(tmp);
5733           }
5734           ok = FALSE;
5735           lives_strfreev(array);
5736           break;
5737         }
5738 
5739         xfilt2 = atoi(array[3]); // sub filter number
5740         if (xfilt >= nfilts) {
5741           if (!prefs->vj_mode) {
5742             d_print((tmp = lives_strdup_printf(_("Invalid in filter %d for link params found in compound effect %s, line %d\n"),
5743                                                xfilt2, plugin_name, line)));
5744             LIVES_ERROR(tmp);
5745             lives_free(tmp);
5746           }
5747           ok = FALSE;
5748           lives_strfreev(array);
5749           break;
5750         }
5751         xfilter = get_weed_filter(filters[xfilt2]);
5752 
5753         nparams = num_in_params(xfilter, FALSE, FALSE);
5754 
5755         pnum2 = atoi(array[4]);
5756 
5757         if (pnum2 >= nparams) {
5758           if (!prefs->vj_mode) {
5759             d_print((tmp = lives_strdup_printf(_("Invalid in param %d for link params found in compound effect %s, line %d\n"),
5760                                                pnum2, plugin_name, line)));
5761             LIVES_ERROR(tmp);
5762             lives_free(tmp);
5763           }
5764           ok = FALSE;
5765           lives_strfreev(array);
5766           break;
5767         }
5768 
5769         // calc xpnum2
5770         xpnum2 = pnum2;
5771         for (i = 0; i < xfilt2; i++) xpnum2 += num_in_params(get_weed_filter(filters[i]), FALSE, FALSE);
5772 
5773         // must get paramtmpl here from filter (not xfilter)
5774         iptmpl = weed_filter_in_paramtmpl(filter, xpnum2, FALSE);
5775 
5776         xvals[0] = xfilt;
5777         xvals[1] = pnum;
5778 
5779         weed_set_int_array(iptmpl, WEED_LEAF_HOST_INTERNAL_CONNECTION, 2, xvals);
5780         if (autoscale == WEED_TRUE) weed_set_boolean_value(iptmpl,
5781               WEED_LEAF_HOST_INTERNAL_CONNECTION_AUTOSCALE, WEED_TRUE);
5782 
5783         lives_strfreev(array);
5784         break;
5785       case 4:
5786         // link alpha channels: format is f0|c0|f1|c1
5787         ntok = get_token_count(buff, '|');
5788 
5789         if (ntok != 4) {
5790           if (!prefs->vj_mode) {
5791             d_print((tmp = lives_strdup_printf(_("Invalid channel link found in compound effect %s, line %d\n"),
5792                                                plugin_name, line)));
5793             LIVES_ERROR(tmp);
5794             lives_free(tmp);
5795           }
5796           ok = FALSE;
5797           break;
5798         }
5799 
5800         array = lives_strsplit(buff, "|", ntok);
5801 
5802         xfilt = atoi(array[0]); // sub filter number
5803         if (xfilt < 0 || xfilt >= nfilts) {
5804           if (!prefs->vj_mode) {
5805             d_print((tmp = lives_strdup_printf(_("Invalid out filter %d for link channels found in compound effect %s, "
5806                                                  "line %d\n"), xfilt, plugin_name, line)));
5807             LIVES_ERROR(tmp);
5808             lives_free(tmp);
5809           }
5810           ok = FALSE;
5811           lives_strfreev(array);
5812           break;
5813         }
5814         xfilter = get_weed_filter(filters[xfilt]);
5815 
5816         nchans = 0;
5817         if (weed_plant_has_leaf(xfilter, WEED_LEAF_OUT_CHANNEL_TEMPLATES)) {
5818           nchans = weed_leaf_num_elements(xfilter, WEED_LEAF_OUT_CHANNEL_TEMPLATES);
5819           if (!weed_get_plantptr_value(xfilter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &error)) nchans = 0;
5820         }
5821 
5822         cnum = atoi(array[1]);
5823 
5824         if (cnum >= nchans) {
5825           if (!prefs->vj_mode) {
5826             d_print((tmp = lives_strdup_printf(_("Invalid out channel %d for link params found in compound effect %s, line %d\n"),
5827                                                cnum, plugin_name, line)));
5828             LIVES_ERROR(tmp);
5829             lives_free(tmp);
5830           }
5831           ok = FALSE;
5832           lives_strfreev(array);
5833           break;
5834         }
5835 
5836         xfilt2 = atoi(array[2]); // sub filter number
5837         if (xfilt2 <= xfilt || xfilt >= nfilts) {
5838           if (!prefs->vj_mode) {
5839             d_print((tmp = lives_strdup_printf(_("Invalid in filter %d for link channels found in compound effect %s, line %d\n"),
5840                                                xfilt2, plugin_name, line)));
5841             LIVES_ERROR(tmp);
5842             lives_free(tmp);
5843           }
5844           ok = FALSE;
5845           lives_strfreev(array);
5846           break;
5847         }
5848         xfilter = get_weed_filter(filters[xfilt2]);
5849 
5850         nchans = 0;
5851         if (weed_plant_has_leaf(xfilter, WEED_LEAF_IN_CHANNEL_TEMPLATES)) {
5852           nchans = weed_leaf_num_elements(xfilter, WEED_LEAF_IN_CHANNEL_TEMPLATES);
5853           if (!weed_get_plantptr_value(xfilter, WEED_LEAF_IN_CHANNEL_TEMPLATES, &error)) nchans = 0;
5854         }
5855 
5856         cnum2 = atoi(array[3]);
5857 
5858         if (cnum2 >= nchans) {
5859           if (!prefs->vj_mode) {
5860             d_print((tmp = lives_strdup_printf(_("Invalid in channel %d for link params found in compound effect %s, line %d\n"),
5861                                                cnum2, plugin_name, line)));
5862             LIVES_ERROR(tmp);
5863             lives_free(tmp);
5864           }
5865           ok = FALSE;
5866           lives_strfreev(array);
5867           break;
5868         }
5869 
5870         xvals[0] = xfilt;
5871         xvals[1] = cnum;
5872         xvals[2] = xfilt2;
5873         xvals[3] = cnum2;
5874 
5875         // unlike with in_param_templates, which are copy by value, in_channel_templates are copy by ref.
5876         // so we need to add some extra leaves to the compound filter to note channel connections
5877         // - we will link the actual channels when we create an instance from the filter
5878         key = lives_strdup_printf(WEED_LEAF_HOST_CHANNEL_CONNECTION "%d", xconx++);
5879         weed_set_int_array(filter, key, 4, xvals);
5880         lives_free(key);
5881 
5882         lives_strfreev(array);
5883         break;
5884       default:
5885         break;
5886       }
5887       if (!ok) {
5888         if (filter) weed_filter_free(filter, NULL);
5889         filter = NULL;
5890         break;
5891       }
5892     }
5893     fclose(cpdfile);
5894   }
5895 
5896   if (filter) {
5897     int idx;
5898     char *filter_name = lives_strdup_printf(_("Compound:%s"), plugin_name);
5899     weed_set_string_value(filter, WEED_LEAF_NAME, filter_name);
5900 
5901     weed_set_string_value(filter, WEED_LEAF_AUTHOR, author);
5902     weed_set_int_value(filter, WEED_LEAF_VERSION, version);
5903     idx = num_weed_filters++;
5904 
5905     weed_filters = (weed_plant_t **)lives_realloc(weed_filters, num_weed_filters * sizeof(weed_plant_t *));
5906     weed_filters[idx] = filter;
5907     hashnames = (lives_hashjoint *)lives_realloc(hashnames, num_weed_filters * sizeof(lives_hashjoint));
5908     gen_hashnames(idx, idx);
5909     lives_free(filter_name);
5910   }
5911 
5912   if (author) lives_free(author);
5913   if (filters) lives_free(filters);
5914 }
5915 
5916 
load_compound_fx(void)5917 static int load_compound_fx(void) {
5918   LiVESList *compound_plugin_list;
5919 
5920   int plugin_idx, onum_filters = num_weed_filters;
5921 
5922   char *lives_compound_plugin_path, *plugin_name, *plugin_path;
5923 
5924   threaded_dialog_spin(0.);
5925 
5926   lives_compound_plugin_path = lives_build_path(prefs->config_datadir, PLUGIN_COMPOUND_EFFECTS_CUSTOM, NULL);
5927   compound_plugin_list = get_plugin_list(PLUGIN_COMPOUND_EFFECTS_CUSTOM, TRUE, lives_compound_plugin_path, NULL);
5928 
5929   for (plugin_idx = 0; plugin_idx < lives_list_length(compound_plugin_list); plugin_idx++) {
5930     plugin_name = (char *)lives_list_nth_data(compound_plugin_list, plugin_idx);
5931     plugin_path = lives_build_path(lives_compound_plugin_path, plugin_name, NULL);
5932     load_compound_plugin(plugin_name, plugin_path);
5933     lives_free(plugin_path);
5934     threaded_dialog_spin(0.);
5935   }
5936 
5937   lives_list_free_all(&compound_plugin_list);
5938 
5939   threaded_dialog_spin(0.);
5940   lives_free(lives_compound_plugin_path);
5941 
5942   lives_compound_plugin_path = lives_build_path(prefs->prefix_dir, PLUGIN_COMPOUND_DIR,
5943                                PLUGIN_COMPOUND_EFFECTS_BUILTIN, NULL);
5944   compound_plugin_list = get_plugin_list(PLUGIN_COMPOUND_EFFECTS_BUILTIN, TRUE, lives_compound_plugin_path, NULL);
5945 
5946   for (plugin_idx = 0; plugin_idx < lives_list_length(compound_plugin_list); plugin_idx++) {
5947     plugin_name = (char *)lives_list_nth_data(compound_plugin_list, plugin_idx);
5948     plugin_path = lives_build_path(lives_compound_plugin_path, plugin_name, NULL);
5949     load_compound_plugin(plugin_name, plugin_path);
5950     lives_free(plugin_path);
5951     threaded_dialog_spin(0.);
5952   }
5953 
5954   lives_list_free_all(&compound_plugin_list);
5955 
5956   if (num_weed_filters > onum_filters) {
5957     d_print(_("Successfully loaded %d compound filters\n"), num_weed_filters - onum_filters);
5958   }
5959 
5960   lives_free(lives_compound_plugin_path);
5961   threaded_dialog_spin(0.);
5962   return num_weed_filters - onum_filters;
5963 }
5964 
5965 
weed_unload_all(void)5966 void weed_unload_all(void) {
5967   weed_plant_t *filter, *plugin_info, *host_info;
5968   LiVESList *pinfo = NULL, *list, *freed_plants = NULL, *hostinfo_ptrs = NULL;
5969 
5970   int error;
5971 
5972   register int i, j;
5973 
5974   if (!fx_inited) return;
5975 
5976   mainw->num_tr_applied = 0;
5977   weed_deinit_all(TRUE);
5978 
5979   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
5980     for (j = 0; j < prefs->max_modes_per_key; j++) {
5981       if (key_defaults[i][j]) free_key_defaults(i, j);
5982     }
5983     lives_free(key_defaults[i]);
5984   }
5985 
5986   THREADVAR(chdir_failed) = FALSE;
5987 
5988   for (i = 0; i < num_weed_filters; i++) {
5989     filter = weed_filters[i];
5990     plugin_info = weed_get_plantptr_value(filter, WEED_LEAF_PLUGIN_INFO, &error);
5991     if (!plugin_info) {
5992       weed_filter_free(filter, &freed_plants);
5993       lives_list_free(freed_plants);
5994       freed_plants = NULL;
5995       continue;
5996     }
5997     freed_plants = weed_get_voidptr_value(plugin_info, WEED_LEAF_FREED_PLANTS, NULL);
5998     weed_filter_free(filter, &freed_plants);
5999     if (!pinfo || lives_list_index(pinfo, plugin_info) == -1) {
6000       pinfo = lives_list_append(pinfo, plugin_info);
6001       weed_set_voidptr_value(plugin_info, WEED_LEAF_FREED_PLANTS, (void *)freed_plants);
6002     }
6003     freed_plants = NULL;
6004   }
6005 
6006   list = pinfo;
6007 
6008   while (pinfo) {
6009     // We need to free ALL the filters for a plugin before calling desetup and dlclose.
6010     // Otherwise we will end doing this on an unloaded plugin !
6011     plugin_info = (weed_plant_t *)pinfo->data;
6012     if (plugin_info) {
6013       void *handle = weed_get_voidptr_value(plugin_info, WEED_LEAF_HOST_HANDLE, &error);
6014       if (handle && prefs->startup_phase == 0) {
6015         weed_desetup_f desetup_fn;
6016         if ((desetup_fn = (weed_desetup_f)dlsym(handle, "weed_desetup")) != NULL) {
6017           char *cwd = cd_to_plugin_dir(plugin_info);
6018           // call weed_desetup()
6019           (*desetup_fn)();
6020           lives_chdir(cwd, FALSE);
6021           lives_free(cwd);
6022         }
6023         dlclose(handle);
6024         threaded_dialog_spin(0.);
6025       }
6026       freed_plants = weed_get_voidptr_value(plugin_info, WEED_LEAF_FREED_PLANTS, NULL);
6027       if (freed_plants) {
6028         lives_list_free(freed_plants);
6029         freed_plants = NULL;
6030       }
6031       host_info = weed_get_plantptr_value((weed_plant_t *)pinfo->data, WEED_LEAF_HOST_INFO, &error);
6032       weed_plant_free_if_not_in_list(host_info, &hostinfo_ptrs);
6033       weed_plant_free(plugin_info);
6034     }
6035     pinfo = pinfo->next;
6036   }
6037 
6038   if (list) lives_list_free(list);
6039   if (hostinfo_ptrs) lives_list_free(hostinfo_ptrs);
6040 
6041   for (i = 0; i < num_weed_filters; i++) {
6042     for (j = 0; j < NHASH_TYPES; j ++) {
6043       if (hashnames[i][j].string) lives_free(hashnames[i][j].string);
6044     }
6045   }
6046 
6047   if (hashnames) lives_free(hashnames);
6048   if (weed_filters) lives_free(weed_filters);
6049 
6050   lives_list_free(weed_fx_sorted_list);
6051 
6052   for (i = 0; i < FX_KEYS_MAX; i++) {
6053     lives_free(key_to_fx[i]);
6054     lives_free(key_to_instance[i]);
6055     lives_free(key_to_instance_copy[i]);
6056   }
6057 
6058   threaded_dialog_spin(0.);
6059 
6060   if (THREADVAR(chdir_failed)) {
6061     char *dirs = (_("Some plugin directories"));
6062     do_chdir_failed_error(dirs);
6063     lives_free(dirs);
6064   }
6065 }
6066 
6067 
weed_in_channels_free(weed_plant_t * inst)6068 static void weed_in_channels_free(weed_plant_t *inst) {
6069   int num_channels;
6070   weed_plant_t **channels = weed_instance_get_in_channels(inst, &num_channels);
6071   if (num_channels > 0) {
6072     for (int i = 0; i < num_channels; i++) {
6073       if (channels[i]) {
6074         if (weed_palette_is_alpha(weed_channel_get_palette(channels[i]))) weed_layer_free((weed_layer_t *)channels[i]);
6075         else weed_plant_free(channels[i]);
6076       }
6077     }
6078   }
6079   lives_free(channels);
6080 }
6081 
6082 
weed_out_channels_free(weed_plant_t * inst)6083 static void weed_out_channels_free(weed_plant_t *inst) {
6084   int num_channels;
6085   weed_plant_t **channels = weed_instance_get_out_channels(inst, &num_channels);
6086   if (num_channels > 0) {
6087     for (int i = 0; i < num_channels; i++) {
6088       if (channels[i]) {
6089         if (weed_palette_is_alpha(weed_channel_get_palette(channels[i]))) weed_layer_free((weed_layer_t *)channels[i]);
6090         else weed_plant_free(channels[i]);
6091       }
6092     }
6093   }
6094   lives_free(channels);
6095 }
6096 
6097 
weed_channels_free(weed_plant_t * inst)6098 static void weed_channels_free(weed_plant_t *inst) {
6099   weed_in_channels_free(inst);
6100   weed_out_channels_free(inst);
6101 }
6102 
6103 
weed_gui_free(weed_plant_t * plant)6104 static void weed_gui_free(weed_plant_t *plant) {
6105   weed_plant_t *gui = weed_get_plantptr_value(plant, WEED_LEAF_GUI, NULL);
6106   if (gui) weed_plant_free(gui);
6107 }
6108 
6109 
weed_in_params_free(weed_plant_t ** parameters,int num_parameters)6110 void weed_in_params_free(weed_plant_t **parameters, int num_parameters) {
6111   for (int i = 0; i < num_parameters; i++) {
6112     if (parameters[i]) {
6113       if (parameters[i] == mainw->rte_textparm) mainw->rte_textparm = NULL;
6114       weed_gui_free(parameters[i]);
6115       weed_plant_free(parameters[i]);
6116     }
6117   }
6118 }
6119 
6120 
weed_in_parameters_free(weed_plant_t * inst)6121 void weed_in_parameters_free(weed_plant_t *inst) {
6122   int num_parameters;
6123   weed_plant_t **parameters = weed_instance_get_in_params(inst, &num_parameters);
6124   if (num_parameters > 0) {
6125     weed_in_params_free(parameters, num_parameters);
6126     weed_leaf_delete(inst, WEED_LEAF_IN_PARAMETERS);
6127   }
6128   lives_free(parameters);
6129 }
6130 
6131 
weed_out_parameters_free(weed_plant_t * inst)6132 static void weed_out_parameters_free(weed_plant_t *inst) {
6133   int num_parameters;
6134   weed_plant_t **parameters = weed_instance_get_out_params(inst, &num_parameters);
6135   if (num_parameters > 0) {
6136     for (int i = 0; i < num_parameters; i++) {
6137       if (parameters[i]) weed_plant_free(parameters[i]);
6138     }
6139     weed_leaf_delete(inst, WEED_LEAF_OUT_PARAMETERS);
6140   }
6141   lives_free(parameters);
6142 }
6143 
6144 
weed_parameters_free(weed_plant_t * inst)6145 static void weed_parameters_free(weed_plant_t *inst) {
6146   weed_in_parameters_free(inst);
6147   weed_out_parameters_free(inst);
6148 }
6149 
6150 
lives_free_instance(weed_plant_t * inst)6151 static void lives_free_instance(weed_plant_t *inst) {
6152   weed_channels_free(inst);
6153   weed_parameters_free(inst);
6154   weed_plant_free(inst);
6155 }
6156 
6157 
_weed_instance_unref(weed_plant_t * inst)6158 LIVES_GLOBAL_INLINE int _weed_instance_unref(weed_plant_t *inst) {
6159   // return new refcount
6160   // return value of -1 indicates the instance was freed
6161   // -2 if the inst was NULL
6162   int nrefs;
6163   if (!inst) return -2;
6164 
6165   pthread_mutex_lock(&mainw->instance_ref_mutex);
6166   nrefs = weed_get_int_value(inst, WEED_LEAF_HOST_REFS, NULL) - 1;
6167 
6168   if (nrefs <= -1) {
6169     char *msg;
6170     pthread_mutex_unlock(&mainw->instance_ref_mutex);
6171     if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_REFS)) {
6172       msg = lives_strdup_printf("unref of filter instance (%p) with nrefs == %d\n", inst, nrefs);
6173       LIVES_ERROR(msg);
6174       break_me("invalid filt inst unref");
6175     }
6176     return nrefs;
6177   }
6178   weed_set_int_value(inst, WEED_LEAF_HOST_REFS, nrefs);
6179   pthread_mutex_unlock(&mainw->instance_ref_mutex);
6180 
6181 #ifdef DEBUG_REFCOUNT
6182   g_print("unref %p, nrefs==%d\n", inst, nrefs);
6183 #endif
6184   if (nrefs == -1) {
6185 #ifdef DEBUG_REFCOUNT
6186     g_print("FREE %p\n", inst);
6187 #endif
6188     lives_free_instance(inst);
6189   }
6190   return nrefs;
6191 }
6192 
6193 
_weed_instance_ref(weed_plant_t * inst)6194 int _weed_instance_ref(weed_plant_t *inst) {
6195   // return refcount after the operation
6196   // or 0 if the inst was NULL
6197   int nrefs;
6198 
6199   if (!inst) return 0;
6200 
6201   pthread_mutex_lock(&mainw->instance_ref_mutex);
6202   nrefs = weed_get_int_value(inst, WEED_LEAF_HOST_REFS, NULL);
6203   weed_set_int_value(inst, WEED_LEAF_HOST_REFS, ++nrefs);
6204 #ifdef DEBUG_REFCOUNT
6205   g_print("ref %p, nrefs==%d\n", inst, nrefs);
6206 #endif
6207   pthread_mutex_unlock(&mainw->instance_ref_mutex);
6208   return nrefs;
6209 }
6210 
6211 
_weed_instance_obtain(int line,char * file,int key,int mode)6212 weed_plant_t *_weed_instance_obtain(int line, char *file, int key, int mode) {
6213   // does not create the instance, but adds a ref to an existing one
6214   // caller MUST call weed_instance_unref() when instance is no longer needed
6215   weed_plant_t *instance;
6216 
6217   if (mode < 0 || mode > rte_key_getmaxmode(key + 1)) return NULL;
6218   pthread_mutex_lock(&mainw->instance_ref_mutex);
6219   instance = key_to_instance[key][mode];
6220 #ifdef DEBUG_REFCOUNT
6221   if (instance) g_print("wio %p at line %d in file %s\n", instance, line, file);
6222 #endif
6223   if (instance) _weed_instance_ref(instance);
6224   pthread_mutex_unlock(&mainw->instance_ref_mutex);
6225   return instance;
6226 }
6227 
6228 
6229 #ifndef DEBUG_REFCOUNT
weed_instance_ref(weed_plant_t * inst)6230 LIVES_GLOBAL_INLINE int weed_instance_ref(weed_plant_t *inst) {
6231   return _weed_instance_ref(inst);
6232 }
6233 
weed_instance_unref(weed_plant_t * inst)6234 LIVES_GLOBAL_INLINE int weed_instance_unref(weed_plant_t *inst) {
6235   return _weed_instance_unref(inst);
6236 }
6237 
weed_instance_obtain(int key,int mode)6238 LIVES_GLOBAL_INLINE weed_plant_t *weed_instance_obtain(int key, int mode) {
6239   return _weed_instance_obtain(0, NULL, key, mode);
6240 }
6241 #endif
6242 
6243 /**
6244    @brief create channels for an instance, using the channel templates and filter class as a guide
6245 
6246     for repeating channels, the selected number should already have been set in WEED_LEAF_HOST_REPEATS
6247     for the template
6248 */
weed_channels_create(weed_plant_t * filter,boolean in)6249 static weed_plant_t **weed_channels_create(weed_plant_t *filter, boolean in) {
6250   weed_plant_t **channels, **chantmpls;
6251   int num_channels;
6252   int i, j;
6253   int ccount = 0;
6254   int num_repeats;
6255   int flags;
6256   boolean pvary = FALSE;
6257 
6258   if (in) chantmpls = weed_filter_get_in_chantmpls(filter, &num_channels);
6259   else chantmpls = weed_filter_get_out_chantmpls(filter, &num_channels);
6260 
6261   for (i = 0; i < num_channels; i++) {
6262     if (weed_plant_has_leaf(chantmpls[i], WEED_LEAF_HOST_REPEATS))
6263       num_repeats = weed_get_int_value(chantmpls[i], WEED_LEAF_HOST_REPEATS, NULL);
6264     else num_repeats = 1;
6265     ccount += num_repeats;
6266   }
6267 
6268   channels = (weed_plant_t **)lives_calloc((ccount + 1), sizeof(weed_plant_t *));
6269 
6270   flags = weed_filter_get_flags(filter);
6271   if (flags & WEED_FILTER_PALETTES_MAY_VARY) {
6272     pvary = TRUE;
6273   }
6274 
6275   ccount = 0;
6276 
6277   for (i = 0; i < num_channels; i++) {
6278     if (weed_plant_has_leaf(chantmpls[i], WEED_LEAF_HOST_REPEATS))
6279       num_repeats = weed_get_int_value(chantmpls[i], WEED_LEAF_HOST_REPEATS, NULL);
6280     else num_repeats = 1;
6281     for (j = 0; j < num_repeats; j++) {
6282       /// set some reasonable defaults for when we call init_func()
6283       channels[ccount] = weed_plant_new(WEED_PLANT_CHANNEL);
6284       weed_set_plantptr_value(channels[ccount], WEED_LEAF_TEMPLATE, chantmpls[i]);
6285       if (weed_chantmpl_is_audio(chantmpls[i]) == WEED_FALSE) {
6286         int rah = ALIGN_DEF, nplanes, width, n, pal;
6287         int rs[4];
6288         weed_set_voidptr_value(channels[ccount], WEED_LEAF_PIXEL_DATA, NULL);
6289         set_channel_size(filter, channels[ccount], DEF_GEN_WIDTH, DEF_GEN_HEIGHT);
6290         pal = WEED_PALETTE_END;
6291         if (pvary) pal = weed_get_int_value(chantmpls[i], WEED_LEAF_PALETTE_LIST, NULL);
6292         if (pal == WEED_PALETTE_NONE) pal = weed_get_int_value(filter, WEED_LEAF_PALETTE_LIST, NULL);
6293         weed_set_int_value(channels[ccount], WEED_LEAF_CURRENT_PALETTE, pal);
6294         if (weed_plant_has_leaf(filter, WEED_LEAF_ALIGNMENT_HINT)) {
6295           rah = weed_get_int_value(filter, WEED_LEAF_ALIGNMENT_HINT, NULL);
6296         }
6297         nplanes = weed_palette_get_nplanes(pal);
6298         width = weed_channel_get_width(channels[ccount]) * (weed_palette_get_bits_per_macropixel(pal) >> 3);
6299         for (n = 0; n < weed_palette_get_nplanes(pal); n++) {
6300           rs[n] = ALIGN_CEIL(width * weed_palette_get_plane_ratio_horizontal(pal, n), rah);
6301         }
6302         weed_set_int_array(channels[ccount], WEED_LEAF_ROWSTRIDES, nplanes, rs);
6303       }
6304       if (weed_plant_has_leaf(chantmpls[i], WEED_LEAF_HOST_DISABLED)) {
6305         weed_set_boolean_value(channels[ccount], WEED_LEAF_DISABLED,
6306                                weed_get_boolean_value(chantmpls[i], WEED_LEAF_HOST_DISABLED, NULL));
6307       }
6308       weed_add_plant_flags(channels[ccount], WEED_FLAG_UNDELETABLE | WEED_FLAG_IMMUTABLE, "plugin_");
6309       ccount++;
6310     }
6311   }
6312   channels[ccount] = NULL;
6313   lives_free(chantmpls);
6314   return channels;
6315 }
6316 
6317 
weed_params_create(weed_plant_t * filter,boolean in)6318 weed_plant_t **weed_params_create(weed_plant_t *filter, boolean in) {
6319   // return set of parameters with default/host_default values
6320   // in==TRUE create in parameters for filter
6321   // in==FALSE create out parameters for filter
6322 
6323   weed_plant_t **params, **paramtmpls;
6324   int num_params;
6325 
6326   if (in) paramtmpls = weed_filter_get_in_paramtmpls(filter, &num_params);
6327   else paramtmpls = weed_filter_get_out_paramtmpls(filter, &num_params);
6328   if (num_params == 0) return NULL;
6329 
6330   params = (weed_plant_t **)lives_malloc((num_params + 1) * sizeof(weed_plant_t *));
6331 
6332   for (int i = 0; i < num_params; i++) {
6333     params[i] = weed_plant_new(WEED_PLANT_PARAMETER);
6334     weed_set_plantptr_value(params[i], WEED_LEAF_TEMPLATE, paramtmpls[i]);
6335     if (in && !weed_paramtmpl_value_irrelevant(paramtmpls[i])) {
6336       if (weed_plant_has_leaf(paramtmpls[i], WEED_LEAF_HOST_DEFAULT)) {
6337         weed_leaf_copy(params[i], WEED_LEAF_VALUE, paramtmpls[i], WEED_LEAF_HOST_DEFAULT);
6338       } else weed_leaf_copy(params[i], WEED_LEAF_VALUE, paramtmpls[i], WEED_LEAF_DEFAULT);
6339       weed_add_plant_flags(params[i], WEED_FLAG_UNDELETABLE | WEED_FLAG_IMMUTABLE, "plugin_");
6340     } else {
6341       weed_leaf_copy(params[i], WEED_LEAF_VALUE, paramtmpls[i], WEED_LEAF_DEFAULT);
6342     }
6343   }
6344   params[num_params] = NULL;
6345   lives_free(paramtmpls);
6346 
6347   return params;
6348 }
6349 
6350 
set_default_channel_sizes(weed_plant_t * filter,weed_plant_t ** in_channels,weed_plant_t ** out_channels)6351 static void set_default_channel_sizes(weed_plant_t *filter, weed_plant_t **in_channels, weed_plant_t **out_channels) {
6352   // set some reasonable default channel sizes when we first init the effect
6353   weed_plant_t *channel, *chantmpl;
6354   boolean has_aud_in_chans = FALSE;
6355   int width, height;
6356 
6357   register int i;
6358 
6359   // ignore filters with no in/out channels (e.g. data processors)
6360   if ((!in_channels || !in_channels[0]) && (!out_channels || !out_channels[0])) return;
6361 
6362   for (i = 0; in_channels && in_channels[i] &&
6363        !(weed_plant_has_leaf(in_channels[i], WEED_LEAF_DISABLED) &&
6364          weed_get_boolean_value(in_channels[i], WEED_LEAF_DISABLED, NULL) == WEED_TRUE); i++) {
6365     channel = in_channels[i];
6366     chantmpl = weed_get_plantptr_value(channel, WEED_LEAF_TEMPLATE, NULL);
6367     if (weed_chantmpl_is_audio(chantmpl) == WEED_FALSE) {
6368       set_channel_size(filter, channel, 320, 240);
6369     } else {
6370       if (mainw->current_file == -1) {
6371         weed_set_int_value(channel, WEED_LEAF_AUDIO_CHANNELS, DEFAULT_AUDIO_CHANS);
6372         weed_set_int_value(channel, WEED_LEAF_AUDIO_RATE, DEFAULT_AUDIO_RATE);
6373       } else {
6374         weed_set_int_value(channel, WEED_LEAF_AUDIO_CHANNELS, cfile->achans);
6375         weed_set_int_value(channel, WEED_LEAF_AUDIO_RATE, cfile->arate);
6376       }
6377       weed_set_int_value(channel, WEED_LEAF_AUDIO_DATA_LENGTH, 0);
6378       weed_set_voidptr_value(channel, WEED_LEAF_AUDIO_DATA, NULL);
6379       has_aud_in_chans = TRUE;
6380     }
6381   }
6382 
6383   for (i = 0; out_channels && out_channels[i]; i++) {
6384     channel = out_channels[i];
6385     chantmpl = weed_channel_get_template(channel);
6386     if (weed_chantmpl_is_audio(chantmpl) == WEED_FALSE) {
6387       width = DEF_FRAME_HSIZE_UNSCALED;
6388       height = DEF_FRAME_VSIZE_UNSCALED;
6389       set_channel_size(filter, channel, width, height);
6390     } else {
6391       if (mainw->current_file == -1 || !has_aud_in_chans) {
6392         weed_set_int_value(channel, WEED_LEAF_AUDIO_CHANNELS, DEFAULT_AUDIO_CHANS);
6393         weed_set_int_value(channel, WEED_LEAF_AUDIO_RATE, DEFAULT_AUDIO_RATE);
6394       } else {
6395         weed_set_int_value(channel, WEED_LEAF_AUDIO_CHANNELS, cfile->achans);
6396         weed_set_int_value(channel, WEED_LEAF_AUDIO_RATE, cfile->arate);
6397       }
6398       weed_set_int_value(channel, WEED_LEAF_AUDIO_DATA_LENGTH, 0);
6399       weed_set_voidptr_value(channel, WEED_LEAF_AUDIO_DATA, NULL);
6400     }
6401   }
6402 }
6403 
6404 
weed_create_instance(weed_plant_t * filter,weed_plant_t ** inc,weed_plant_t ** outc,weed_plant_t ** inp,weed_plant_t ** outp)6405 static weed_plant_t *weed_create_instance(weed_plant_t *filter, weed_plant_t **inc, weed_plant_t **outc,
6406     weed_plant_t **inp, weed_plant_t **outp) {
6407   // here we create a new filter_instance from the ingredients:
6408   // filter_class, in_channels, out_channels, in_parameters, out_parameters
6409   weed_plant_t *inst = weed_plant_new(WEED_PLANT_FILTER_INSTANCE);
6410 
6411   int flags = WEED_FLAG_IMMUTABLE | WEED_FLAG_UNDELETABLE, n;
6412 
6413   weed_set_plantptr_value(inst, WEED_LEAF_FILTER_CLASS, filter);
6414   if (inc) weed_set_plantptr_array(inst, WEED_LEAF_IN_CHANNELS, weed_flagset_array_count(inc, TRUE), inc);
6415   if (outc) weed_set_plantptr_array(inst, WEED_LEAF_OUT_CHANNELS, weed_flagset_array_count(outc, TRUE), outc);
6416   if (inp) weed_set_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, weed_flagset_array_count(inp, TRUE), inp);
6417   if (outp) {
6418     weed_set_plantptr_array(inst, WEED_LEAF_OUT_PARAMETERS, (n = weed_flagset_array_count(outp, TRUE)), outp);
6419     for (int i = 0; i < n; i++) {
6420       // allow plugins to set out_param WEED_LEAF_VALUE
6421       weed_leaf_set_flags(outp[i], WEED_LEAF_VALUE, (weed_leaf_get_flags(outp[i], WEED_LEAF_VALUE) | flags) ^ flags);
6422     }
6423   }
6424 
6425   weed_set_boolean_value(inst, WEED_LEAF_HOST_INITED, WEED_FALSE);
6426   weed_add_plant_flags(inst, WEED_FLAG_IMMUTABLE | WEED_FLAG_UNDELETABLE, "plugin_");
6427   return inst;
6428 }
6429 
6430 
add_param_connections(weed_plant_t * inst)6431 void add_param_connections(weed_plant_t *inst) {
6432   weed_plant_t **in_ptmpls, **outp;
6433   weed_plant_t *iparam, *oparam, *first_inst = inst, *filter = weed_instance_get_filter(inst, TRUE);
6434   int *xvals;
6435   int nptmpls, error;
6436 
6437   register int i;
6438 
6439   in_ptmpls = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, &nptmpls);
6440   if (!in_ptmpls) return;
6441 
6442   for (i = 0; i < nptmpls; i++) {
6443     if (weed_plant_has_leaf(in_ptmpls[i], WEED_LEAF_HOST_INTERNAL_CONNECTION)) {
6444       xvals = weed_get_int_array(in_ptmpls[i], WEED_LEAF_HOST_INTERNAL_CONNECTION, &error);
6445 
6446       iparam = weed_inst_in_param(first_inst, i, FALSE, FALSE);
6447 
6448       if (xvals[0] > -1) {
6449         inst = first_inst;
6450         while (--xvals[0] >= 0) inst = get_next_compound_inst(inst);
6451 
6452         outp = weed_get_plantptr_array(inst, WEED_LEAF_OUT_PARAMETERS, &error);
6453         oparam = outp[xvals[1]];
6454         lives_free(outp);
6455       } else oparam = iparam; // just hide the parameter, but don't pull a value
6456 
6457       weed_set_plantptr_value(iparam, WEED_LEAF_HOST_INTERNAL_CONNECTION, oparam);
6458 
6459       if (weed_plant_has_leaf(in_ptmpls[i], WEED_LEAF_HOST_INTERNAL_CONNECTION_AUTOSCALE) &&
6460           weed_get_boolean_value(in_ptmpls[i], WEED_LEAF_HOST_INTERNAL_CONNECTION_AUTOSCALE, &error) == WEED_TRUE)
6461         weed_set_boolean_value(iparam, WEED_LEAF_HOST_INTERNAL_CONNECTION_AUTOSCALE, WEED_TRUE);
6462       lives_free(xvals);
6463     }
6464   }
6465   lives_free(in_ptmpls);
6466 }
6467 
6468 
weed_instance_from_filter(weed_plant_t * filter)6469 weed_plant_t *weed_instance_from_filter(weed_plant_t *filter) {
6470   // return an instance from a filter, with the (first if compound) instance refcounted
6471   // caller should call weed_instance_unref() when done
6472   weed_plant_t **inc = NULL, **outc = NULL, **inp = NULL, **outp = NULL, **xinp;
6473 
6474   weed_plant_t *last_inst = NULL, *first_inst = NULL, *inst, *ofilter = filter;
6475   weed_plant_t *ochan = NULL;
6476 
6477   int *filters = NULL, *xvals;
6478 
6479   char *key;
6480 
6481   int nfilters, ninpar = 0, error;
6482 
6483   int xcnumx = 0, x, poffset = 0;
6484 
6485   int i, j;
6486 
6487   if ((nfilters = num_compound_fx(filter)) > 1) {
6488     filters = weed_get_int_array(filter, WEED_LEAF_HOST_FILTER_LIST, NULL);
6489   }
6490 
6491   inp = weed_params_create(filter, TRUE);
6492 
6493   for (i = 0; i < nfilters; i++) {
6494     if (filters) {
6495       filter = weed_filters[filters[i]];
6496     }
6497 
6498     // create channels from channel_templates
6499     inc = weed_channels_create(filter, TRUE);
6500     outc = weed_channels_create(filter, FALSE);
6501 
6502     set_default_channel_sizes(filter, inc, outc); // we set the initial channel sizes to some reasonable defaults
6503 
6504     // create parameters from parameter_templates
6505     outp = weed_params_create(filter, FALSE);
6506 
6507     if (nfilters == 1) xinp = inp;
6508     else {
6509       // for a compound filter, assign only the params which belong to the instance
6510       if (ninpar == 0) xinp = NULL;
6511       else {
6512         xinp = (weed_plant_t **)lives_malloc((ninpar + 1) * sizeof(weed_plant_t *));
6513         x = 0;
6514         for (j = poffset; j < poffset + ninpar; j++) xinp[x++] = inp[j];
6515         xinp[x] = NULL;
6516         poffset += ninpar;
6517       }
6518     }
6519 
6520     if (i == 0) pthread_mutex_lock(&mainw->instance_ref_mutex);
6521     inst = weed_create_instance(filter, inc, outc, xinp, outp);
6522 
6523     if (i == 0) {
6524       weed_instance_ref(inst);
6525       pthread_mutex_unlock(&mainw->instance_ref_mutex);
6526     }
6527 
6528     if (filters) weed_set_plantptr_value(inst, WEED_LEAF_HOST_COMPOUND_CLASS, ofilter);
6529 
6530     if (i > 0) {
6531       weed_set_plantptr_value(last_inst, WEED_LEAF_HOST_NEXT_INSTANCE, inst);
6532     } else first_inst = inst;
6533 
6534     last_inst = inst;
6535 
6536     lives_freep((void **)&inc);
6537     lives_freep((void **)&outc);
6538     lives_freep((void **)&outp);
6539     if (xinp != inp) lives_freep((void **)&xinp);
6540 
6541     /* for (j = 0; j < ninpar; j++) { */
6542     /*   // copy param values if that was set up */
6543     /*   set_copy_to(inst, j, TRUE); */
6544     /* } */
6545   }
6546 
6547   lives_freep((void **)&inp);
6548 
6549   if (filters) {
6550     lives_free(filters);
6551 
6552     // for compound fx, add param and channel connections
6553     filter = ofilter;
6554 
6555     // add param connections
6556     add_param_connections(first_inst);
6557 
6558     while (1) {
6559       // add channel connections
6560       key = lives_strdup_printf(WEED_LEAF_HOST_CHANNEL_CONNECTION "%d", xcnumx++);
6561       if (weed_plant_has_leaf(filter, key)) {
6562         xvals = weed_get_int_array(filter, key, &error);
6563 
6564         inst = first_inst;
6565         while (1) {
6566           if (xvals[0]-- == 0) {
6567             // got the out instance
6568             outc = weed_get_plantptr_array(inst, WEED_LEAF_OUT_CHANNELS, &error);
6569             ochan = outc[xvals[1]];
6570             lives_freep((void **)&outc);
6571           }
6572           if (xvals[2]-- == 0) {
6573             // got the in instance
6574             inc = weed_get_plantptr_array(inst, WEED_LEAF_IN_CHANNELS, &error);
6575             weed_set_plantptr_value(inc[xvals[3]], WEED_LEAF_HOST_INTERNAL_CONNECTION, ochan);
6576             lives_freep((void **)&inc);
6577             break;
6578           }
6579 
6580           inst = get_next_compound_inst(inst);
6581         }
6582 
6583         lives_free(xvals);
6584         lives_free(key);
6585       } else {
6586         lives_free(key);
6587         break;
6588       }
6589     }
6590   }
6591 
6592   return first_inst;
6593 }
6594 
6595 
weed_init_effect(int hotkey)6596 boolean weed_init_effect(int hotkey) {
6597   // mainw->osc_block should be set to TRUE before calling this function !
6598   // filter_mutex MUST be locked, on FALSE return is unlocked
6599   weed_plant_t *filter, *gui;
6600   weed_plant_t *new_instance, *inst;
6601   weed_plant_t *event_list;
6602   weed_error_t error;
6603 
6604   boolean fg_modeswitch = FALSE, is_trans = FALSE, gen_start = FALSE, is_modeswitch = FALSE;
6605   boolean all_out_alpha;
6606   boolean is_gen = FALSE;
6607   boolean is_audio_gen = FALSE;
6608 
6609   int num_tr_applied;
6610   int rte_keys = mainw->rte_keys;
6611   int inc_count, outc_count;
6612   int ntracks;
6613   int idx;
6614   int playing_file = mainw->playing_file;
6615 
6616   mainw->error = FALSE;
6617 
6618   if (hotkey < 0) {
6619     is_modeswitch = TRUE;
6620     hotkey = -hotkey - 1;
6621   }
6622 
6623   if (hotkey == fg_generator_key) {
6624     fg_modeswitch = TRUE;
6625   }
6626 
6627   if (hotkey >= FX_KEYS_MAX) {
6628     mainw->error = TRUE;
6629     return FALSE;
6630   }
6631 
6632   if (!rte_key_valid(hotkey + 1, FALSE)) {
6633     mainw->error = TRUE;
6634     return FALSE;
6635   }
6636 
6637   idx = key_to_fx[hotkey][key_modes[hotkey]];
6638   filter = weed_filters[idx];
6639 
6640   inc_count = enabled_in_channels(filter, FALSE);
6641   outc_count = enabled_out_channels(filter, FALSE);
6642 
6643   if (all_ins_alpha(filter, TRUE)) inc_count = 0;
6644 
6645   if (inc_count == 0) is_gen = TRUE;
6646 
6647   // check first if it is an audio generator
6648 
6649   if ((is_gen || (has_audio_chans_in(filter, FALSE) && !has_video_chans_in(filter, TRUE)))
6650       && has_audio_chans_out(filter, FALSE) && !has_video_chans_out(filter, TRUE)) {
6651     if (!is_realtime_aplayer(prefs->audio_player)) {
6652       // audio fx only with realtime players
6653       char *fxname = weed_filter_idx_get_name(idx, FALSE, FALSE);
6654       d_print(_("Effect %s cannot be used with this audio player.\n"), fxname);
6655       lives_free(fxname);
6656       mainw->error = TRUE;
6657       filter_mutex_unlock(hotkey);
6658       return FALSE;
6659     }
6660 
6661     if (is_gen) {
6662       if (mainw->agen_key != 0) {
6663         // we had an existing audio gen running - stop that one first
6664         int agen_key = mainw->agen_key - 1;
6665         boolean needs_unlock = FALSE;
6666         if (!filter_mutex_trylock(agen_key)) {
6667           needs_unlock = TRUE;
6668         }
6669 
6670         weed_deinit_effect(agen_key);
6671 
6672         pthread_mutex_lock(&mainw->event_list_mutex);
6673         if ((rte_key_is_enabled(1 + agen_key))) {
6674           // need to do this in case starting another audio gen caused us to come here
6675           mainw->rte ^= (GU641 << agen_key);
6676           pthread_mutex_unlock(&mainw->event_list_mutex);
6677           if (rte_window && !mainw->is_rendering && !mainw->multitrack) {
6678             rtew_set_keych(agen_key, FALSE);
6679           }
6680           if (mainw->ce_thumbs) ce_thumbs_set_keych(agen_key, FALSE);
6681         } else pthread_mutex_unlock(&mainw->event_list_mutex);
6682         if (needs_unlock) filter_mutex_unlock(agen_key);
6683       }
6684       is_audio_gen = TRUE;
6685     }
6686   }
6687 
6688   // if outputs are all alpha it is not a true (video/audio generating) generator
6689   all_out_alpha = all_outs_alpha(filter, TRUE);
6690 
6691   // TODO - block template channel changes
6692   // we must stop any old generators
6693 
6694   if (hotkey < FX_KEYS_MAX_VIRTUAL) {
6695     if (!all_out_alpha && is_gen && outc_count > 0 && hotkey != fg_generator_key && mainw->num_tr_applied > 0 &&
6696         mainw->blend_file != -1 && mainw->blend_file != mainw->current_file &&
6697         mainw->files[mainw->blend_file] &&
6698         mainw->files[mainw->blend_file]->clip_type == CLIP_TYPE_GENERATOR && !is_audio_gen) {
6699       /////////////////////////////////////// if blend_file is a generator we should finish it
6700       if (bg_gen_to_start == -1) {
6701         weed_generator_end((weed_plant_t *)mainw->files[mainw->blend_file]->ext_src);
6702       }
6703 
6704       bg_gen_to_start = bg_generator_key = bg_generator_mode = -1;
6705       mainw->blend_file = -1;
6706     }
6707 
6708     if (CURRENT_CLIP_IS_VALID && cfile->clip_type == CLIP_TYPE_GENERATOR &&
6709         (fg_modeswitch || (is_gen && outc_count > 0 && mainw->num_tr_applied == 0)) && !is_audio_gen && !all_out_alpha) {
6710       if (mainw->is_processing || mainw->preview) {
6711         mainw->error = TRUE;
6712         filter_mutex_unlock(hotkey);
6713         return FALSE; // stopping fg gen will cause clip to switch
6714       }
6715       if (LIVES_IS_PLAYING && mainw->whentostop == STOP_ON_VID_END && !is_gen) {
6716         // stop on vid end, and the playing generator stopped
6717         mainw->cancelled = CANCEL_GENERATOR_END;
6718       } else {
6719         if (is_gen && mainw->whentostop == STOP_ON_VID_END) mainw->whentostop = NEVER_STOP;
6720         //////////////////////////////////// switch from one generator to another: keep playing and stop the old one
6721         weed_generator_end((weed_plant_t *)cfile->ext_src);
6722         fg_generator_key = fg_generator_clip = fg_generator_mode = -1;
6723         if (CURRENT_CLIP_IS_VALID && (cfile->achans == 0 || cfile->frames > 0)) {
6724           // in case we switched to bg clip, and bg clip was gen
6725           // otherwise we will get killed in generator_start
6726           mainw->current_file = -1;
6727 	  // *INDENT-OFF*
6728         }}}}
6729   // *INDENT-ON*
6730 
6731   if (hotkey < FX_KEYS_MAX_VIRTUAL) {
6732     if (inc_count == 2) {
6733       mainw->num_tr_applied++; // increase trans count
6734       if (mainw->active_sa_clips == SCREEN_AREA_FOREGROUND) {
6735         mainw->active_sa_clips = SCREEN_AREA_BACKGROUND;
6736       }
6737       if (mainw->ce_thumbs) ce_thumbs_liberate_clip_area_register(SCREEN_AREA_BACKGROUND);
6738       if (mainw->num_tr_applied == 1 && !is_modeswitch) {
6739         mainw->blend_file = mainw->current_file;
6740       }
6741     } else if (is_gen && outc_count > 0 && !is_audio_gen && !all_out_alpha) {
6742       // aha - a generator
6743       if (!LIVES_IS_PLAYING) {
6744         // if we are not playing, we will postpone creating the instance
6745         // this is a workaround for a problem in libvisual
6746         fg_gen_to_start = hotkey;
6747         fg_generator_key = hotkey;
6748         fg_generator_mode = key_modes[hotkey];
6749         gen_start = TRUE;
6750       } else if (!fg_modeswitch && mainw->num_tr_applied == 0 && (mainw->is_processing || mainw->preview))  {
6751         mainw->error = TRUE;
6752         filter_mutex_unlock(hotkey);
6753         return FALSE;
6754       }
6755     }
6756   }
6757   // TODO - unblock template channel changes
6758 
6759   // if the param window is already open, use instance from there
6760   if (fx_dialog[1] && fx_dialog[1]->key == hotkey && fx_dialog[1]->mode == key_modes[hotkey]) {
6761     lives_rfx_t *rfx = fx_dialog[1]->rfx;
6762     new_instance = (weed_plant_t *)rfx->source;
6763     // add a ref since we will remove one below, the param window should already have a ref
6764     weed_instance_ref(new_instance);
6765     update_widget_vis(NULL, hotkey, key_modes[hotkey]); // redraw our paramwindow
6766   } else {
6767     new_instance = weed_instance_from_filter(filter); //adds a ref
6768     // if it is a key effect, set key defaults
6769     if (hotkey < FX_KEYS_MAX_VIRTUAL && key_defaults[hotkey][key_modes[hotkey]]) {
6770       // TODO - handle compound fx
6771       filter_mutex_unlock(hotkey);
6772       weed_reinit_effect(new_instance, FALSE);
6773       filter_mutex_lock(hotkey);
6774       apply_key_defaults(new_instance, hotkey, key_modes[hotkey]);
6775       filter_mutex_unlock(hotkey);
6776       weed_reinit_effect(new_instance, FALSE);
6777       filter_mutex_lock(hotkey);
6778     }
6779   }
6780 
6781   // record the key so we know whose parameters to record later
6782   weed_set_int_value(new_instance, WEED_LEAF_HOST_KEY, hotkey);
6783 
6784   inst = new_instance;
6785 
6786   gui = weed_instance_get_gui(inst, FALSE);
6787   if (gui && weed_get_int_value(gui, WEED_LEAF_EASE_OUT, NULL)) {
6788     weed_leaf_delete(gui, WEED_LEAF_EASE_OUT);
6789   }
6790   weed_leaf_delete(inst, WEED_LEAF_HOST_EASE_OUT_COUNT);
6791   weed_leaf_delete(inst, WEED_LEAF_AUTO_EASING);
6792 
6793   if (weed_plant_has_leaf(filter, WEED_LEAF_HOST_FPS))
6794     weed_leaf_copy(inst, WEED_LEAF_TARGET_FPS, filter, WEED_LEAF_HOST_FPS);
6795   else if (weed_plant_has_leaf(filter, WEED_LEAF_PREFERRED_FPS))
6796     weed_leaf_copy(inst, WEED_LEAF_TARGET_FPS, filter, WEED_LEAF_PREFERRED_FPS);
6797 
6798   while (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE)) {
6799     // handle compound fx
6800     inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, &error);
6801     weed_set_int_value(inst, WEED_LEAF_HOST_KEY, hotkey);
6802   }
6803   inst = new_instance;
6804 
6805   if (!gen_start) {
6806     weed_plant_t *inst, *next_inst = NULL;
6807     inst = new_instance;
6808 
6809 start1:
6810 
6811     filter = weed_instance_get_filter(inst, FALSE);
6812 
6813     if (weed_plant_has_leaf(filter, WEED_LEAF_INIT_FUNC)) {
6814       weed_init_f init_func;
6815       char *cwd = cd_to_plugin_dir(filter), *tmp;
6816       init_func = (weed_init_f)weed_get_funcptr_value(filter, WEED_LEAF_INIT_FUNC, NULL);
6817       if (init_func && (error = (*init_func)(inst)) != WEED_SUCCESS) {
6818         char *filter_name;
6819         filter = weed_filters[idx];
6820         filter_name = weed_get_string_value(filter, WEED_LEAF_NAME, NULL);
6821         d_print(_("Failed to start instance %s, (%s)\n"), filter_name, (tmp = weed_error_to_text(error)));
6822         lives_free(tmp);
6823         lives_free(filter_name);
6824         key_to_instance[hotkey][key_modes[hotkey]] = NULL;
6825 
6826 deinit2:
6827 
6828         if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE))
6829           next_inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, &error);
6830         else next_inst = NULL;
6831 
6832         weed_call_deinit_func(inst);
6833 
6834         if (next_inst && weed_get_boolean_value(next_inst, WEED_LEAF_HOST_INITED, &error) == WEED_TRUE) {
6835           // handle compound fx
6836           inst = next_inst;
6837           goto deinit2;
6838         }
6839 
6840         inst = new_instance;
6841 
6842         if (hotkey < FX_KEYS_MAX_VIRTUAL) {
6843           if (is_trans) {
6844             // TODO: - do we need this ? is_trans is always FALSE !
6845             mainw->num_tr_applied--;
6846             if (mainw->num_tr_applied == 0) {
6847               if (mainw->ce_thumbs) ce_thumbs_liberate_clip_area_register(SCREEN_AREA_FOREGROUND);
6848               if (mainw->active_sa_clips == SCREEN_AREA_BACKGROUND) {
6849                 mainw->active_sa_clips = SCREEN_AREA_FOREGROUND;
6850 		// *INDENT-OFF*
6851               }}}}
6852 	  // *INDENT-ON*
6853 
6854         weed_instance_unref(inst);
6855         lives_chdir(cwd, FALSE);
6856         lives_free(cwd);
6857         if (is_audio_gen) mainw->agen_needs_reinit = FALSE;
6858         mainw->error = TRUE;
6859         filter_mutex_unlock(hotkey);
6860         return FALSE;
6861       }
6862       lives_chdir(cwd, FALSE);
6863       lives_free(cwd);
6864     }
6865 
6866     weed_set_boolean_value(inst, WEED_LEAF_HOST_INITED, WEED_TRUE);
6867     weed_set_boolean_value(inst, WEED_LEAF_HOST_UNUSED, WEED_TRUE);
6868 
6869     if ((inst = get_next_compound_inst(inst)) != NULL) goto start1;
6870 
6871     inst = new_instance;
6872     filter = weed_filters[idx];
6873   }
6874 
6875   if (is_gen && outc_count > 0 && !is_audio_gen && !all_out_alpha) {
6876     // generator start
6877     if (mainw->num_tr_applied > 0 && !fg_modeswitch && mainw->current_file > -1 && LIVES_IS_PLAYING) {
6878       // transition is on, make into bg clip
6879       bg_generator_key = hotkey;
6880       bg_generator_mode = key_modes[hotkey];
6881     } else {
6882       // no transition, make fg (or kb was grabbed for fg generator)
6883       fg_generator_key = hotkey;
6884       fg_generator_mode = key_modes[hotkey];
6885     }
6886 
6887     // start the generator and maybe start playing
6888     num_tr_applied = mainw->num_tr_applied;
6889     if (fg_modeswitch) mainw->num_tr_applied = 0; // force to fg
6890 
6891     key_to_instance[hotkey][key_modes[hotkey]] = inst;
6892     // enable param recording, in case the instance was obtained from a param window
6893     if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NORECORD)) weed_leaf_delete(inst, WEED_LEAF_HOST_NORECORD);
6894 
6895     error = weed_generator_start(inst, hotkey);
6896     if (error != 0) {
6897       int weed_error;
6898       char *filter_name;
6899       filter_mutex_lock(hotkey);
6900       weed_call_deinit_func(inst);
6901       weed_instance_unref(inst);
6902       weed_instance_unref(inst);
6903       if (error != 2) {
6904         filter_name = weed_get_string_value(filter, WEED_LEAF_NAME, &weed_error);
6905         d_print(_("Unable to start generator %s (error code: %d)\n"), filter_name, error);
6906         lives_free(filter_name);
6907       } else mainw->error = TRUE;
6908       if (mainw->num_tr_applied && mainw->current_file > -1) {
6909         bg_gen_to_start = bg_generator_key = bg_generator_mode = -1;
6910       } else {
6911         fg_generator_key = fg_generator_clip = fg_generator_mode = -1;
6912       }
6913       if (fg_modeswitch) mainw->num_tr_applied = num_tr_applied;
6914       key_to_instance[hotkey][key_modes[hotkey]] = NULL;
6915       filter_mutex_unlock(hotkey);
6916       if (!mainw->multitrack) {
6917         if (!LIVES_IS_PLAYING) {
6918           switch_clip(1, mainw->current_file, TRUE);
6919         }
6920       }
6921       return FALSE;
6922     }
6923 
6924     if (playing_file == -1 && mainw->gen_started_play) {
6925       return TRUE;
6926     }
6927 
6928     // weed_generator_start can change the instance
6929     //inst = weed_instance_obtain(hotkey, key_modes[hotkey]);/////
6930 
6931     // TODO - problem if modeswitch triggers playback
6932     // hence we do not allow mixing of generators and non-gens on the same key
6933     if (fg_modeswitch) mainw->num_tr_applied = num_tr_applied;
6934     if (fg_generator_key != -1) {
6935       pthread_mutex_lock(&mainw->event_list_mutex);
6936       mainw->rte |= (GU641 << fg_generator_key);
6937       pthread_mutex_unlock(&mainw->event_list_mutex);
6938       mainw->clip_switched = TRUE;
6939     }
6940     if (bg_generator_key != -1 && !fg_modeswitch) {
6941       pthread_mutex_lock(&mainw->event_list_mutex);
6942       mainw->rte |= (GU641 << bg_generator_key);
6943       pthread_mutex_unlock(&mainw->event_list_mutex);
6944       if (hotkey < prefs->rte_keys_virtual) {
6945         if (rte_window && !mainw->is_rendering && !mainw->multitrack) {
6946           rtew_set_keych(bg_generator_key, TRUE);
6947         }
6948         if (mainw->ce_thumbs) ce_thumbs_set_keych(bg_generator_key, TRUE);
6949       }
6950     }
6951   }
6952 
6953   if (rte_keys == hotkey) {
6954     mainw->rte_keys = rte_keys;
6955     mainw->blend_factor = weed_get_blend_factor(rte_keys);
6956   }
6957 
6958   // need to do this *before* calling append_filter_map_event
6959   key_to_instance[hotkey][key_modes[hotkey]] = inst;
6960 
6961   // enable param recording, in case the instance was obtained from a param window
6962   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NORECORD)) weed_leaf_delete(inst, WEED_LEAF_HOST_NORECORD);
6963 
6964   if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS) && !is_gen) {
6965     ticks_t actual_ticks = mainw->startticks; ///< use the "thoretical" time
6966     uint64_t rteval, new_rte;
6967     pthread_mutex_lock(&mainw->event_list_mutex);
6968     event_list = append_filter_init_event(mainw->event_list, actual_ticks, idx, -1, hotkey, inst);
6969     if (!mainw->event_list) mainw->event_list = event_list;
6970     init_events[hotkey] = get_last_event(mainw->event_list);
6971     ntracks = weed_leaf_num_elements(init_events[hotkey], WEED_LEAF_IN_TRACKS);
6972     pchains[hotkey] = filter_init_add_pchanges(mainw->event_list, inst, init_events[hotkey], ntracks, 0);
6973     rteval = mainw->rte;
6974     new_rte = GU641 << (hotkey);
6975     if (!(rteval & new_rte)) rteval |= new_rte;
6976     create_filter_map(rteval); // we create filter_map event_t * array with ordered effects
6977     mainw->event_list = append_filter_map_event(mainw->event_list, actual_ticks, filter_map);
6978     pthread_mutex_unlock(&mainw->event_list_mutex);
6979   }
6980 
6981   weed_instance_unref(inst); // release the ref we added earlier
6982 
6983   if (is_audio_gen) {
6984     mainw->agen_key = hotkey + 1;
6985     mainw->agen_needs_reinit = FALSE;
6986 
6987     if (mainw->playing_file > 0) {
6988       if (mainw->whentostop == STOP_ON_AUD_END) mainw->whentostop = STOP_ON_VID_END;
6989       if (prefs->audio_player == AUD_PLAYER_JACK) {
6990 #ifdef ENABLE_JACK
6991         if (mainw->jackd_read && mainw->jackd_read->in_use &&
6992             (mainw->jackd_read->playing_file == -1 || mainw->jackd_read->playing_file == mainw->ascrap_file)) {
6993           // if playing external audio, switch over to internal for an audio gen
6994           jack_time_reset(mainw->jackd, mainw->currticks);
6995           // close the reader
6996           jack_rec_audio_end(!prefs->perm_audio_reader, FALSE);
6997         }
6998         if (mainw->jackd && (!mainw->jackd_read || !mainw->jackd_read->in_use)) {
6999           // enable writer
7000           mainw->jackd->in_use = TRUE;
7001         }
7002 #endif
7003       }
7004       if (prefs->audio_player == AUD_PLAYER_PULSE) {
7005 #ifdef HAVE_PULSE_AUDIO
7006         if (mainw->pulsed_read && mainw->pulsed_read->in_use &&
7007             (mainw->pulsed_read->playing_file == -1 || mainw->pulsed_read->playing_file == mainw->ascrap_file)) {
7008           // if playing external audio, switch over to internal for an audio gen
7009           ticks_t audio_ticks = lives_pulse_get_time(mainw->pulsed_read);
7010           if (audio_ticks == -1) {
7011             mainw->cancelled = handle_audio_timeout();
7012             return mainw->cancelled;
7013           }
7014           pa_time_reset(mainw->pulsed, -audio_ticks);
7015           pulse_rec_audio_end(!prefs->perm_audio_reader, FALSE);
7016           pulse_driver_uncork(mainw->pulsed);
7017         }
7018         if (mainw->pulsed && (!mainw->pulsed_read || !mainw->pulsed_read->in_use)) {
7019           mainw->pulsed->in_use = TRUE;
7020         }
7021 #endif
7022       }
7023 
7024       if (LIVES_IS_PLAYING && mainw->record && !mainw->record_paused && prefs->audio_src != AUDIO_SRC_EXT &&
7025           (prefs->rec_opts & REC_AUDIO)) {
7026         // if recording audio, open ascrap file and add audio event
7027         mainw->record = FALSE;
7028         on_record_perf_activate(NULL, NULL);
7029         mainw->record_starting = FALSE;
7030         mainw->record = TRUE;
7031         if (prefs->audio_player == AUD_PLAYER_JACK) {
7032 #ifdef ENABLE_JACK
7033           jack_time_reset(mainw->jackd, lives_jack_get_time(mainw->jackd_read));
7034           mainw->jackd->in_use = TRUE;
7035 #endif
7036 	  // *INDENT-OFF*
7037         }}}}
7038   // *INDENT-ON*
7039 
7040   return TRUE;
7041 }
7042 
7043 
weed_call_init_func(weed_plant_t * inst)7044 weed_error_t weed_call_init_func(weed_plant_t *inst) {
7045   weed_plant_t *filter;
7046   weed_error_t error = WEED_SUCCESS;
7047 
7048   weed_instance_ref(inst);
7049 
7050   filter = weed_instance_get_filter(inst, FALSE);
7051   if (weed_plant_has_leaf(filter, WEED_LEAF_INIT_FUNC)) {
7052     weed_init_f init_func = (weed_init_f)weed_get_funcptr_value(filter, WEED_LEAF_INIT_FUNC, NULL);
7053     if (init_func) {
7054       char *cwd = cd_to_plugin_dir(filter);
7055       error = (*init_func)(inst);
7056       lives_chdir(cwd, FALSE);
7057       lives_free(cwd);
7058     }
7059   }
7060   weed_set_boolean_value(inst, WEED_LEAF_HOST_INITED, WEED_TRUE);
7061   weed_set_boolean_value(inst, WEED_LEAF_HOST_UNUSED, WEED_TRUE);
7062   weed_instance_unref(inst);
7063   return error;
7064 }
7065 
7066 
weed_call_deinit_func(weed_plant_t * instance)7067 weed_error_t weed_call_deinit_func(weed_plant_t *instance) {
7068   // filter_mutex MUST be locked
7069   weed_plant_t *filter;
7070   weed_error_t error = WEED_SUCCESS;
7071 
7072   weed_instance_ref(instance);
7073 
7074   filter = weed_instance_get_filter(instance, FALSE);
7075   if (weed_get_boolean_value(instance, WEED_LEAF_HOST_INITED, NULL) == WEED_FALSE) {
7076     weed_instance_unref(instance);
7077     return 1;
7078   }
7079   if (weed_plant_has_leaf(filter, WEED_LEAF_DEINIT_FUNC)) {
7080     weed_deinit_f deinit_func = (weed_deinit_f)weed_get_funcptr_value(filter, WEED_LEAF_DEINIT_FUNC, NULL);
7081     if (deinit_func) {
7082       char *cwd = cd_to_plugin_dir(filter);
7083       error = (*deinit_func)(instance);
7084       lives_chdir(cwd, FALSE);
7085       lives_free(cwd);
7086     }
7087   }
7088   weed_set_boolean_value(instance, WEED_LEAF_HOST_INITED, WEED_FALSE);
7089   weed_instance_unref(instance);
7090   return error;
7091 }
7092 
7093 
weed_deinit_effect(int hotkey)7094 boolean weed_deinit_effect(int hotkey) {
7095   // hotkey is 0 based
7096   // mainw->osc_block should be set before calling this function !
7097   // caller should also handle mainw->rte
7098 
7099   // filter_mutex_lock(hotkey) must be be called before entering
7100 
7101   weed_plant_t *instance, *inst, *last_inst, *next_inst, *filter, *gui;
7102 
7103   boolean is_modeswitch = FALSE;
7104   boolean was_transition = FALSE;
7105   boolean is_audio_gen = FALSE;
7106   boolean is_video_gen = FALSE;
7107   int num_in_chans, num_out_chans;
7108 
7109   int error;
7110   int easing;
7111 
7112   int needs_unlock = -1;
7113 
7114   if (hotkey < 0) {
7115     is_modeswitch = TRUE;
7116     hotkey = -hotkey - 1;
7117   }
7118 
7119   if (hotkey >= FX_KEYS_MAX) return FALSE;
7120 
7121   // adds a ref
7122   if (!(instance = weed_instance_obtain(hotkey, key_modes[hotkey]))) return TRUE;
7123 
7124   if (LIVES_IS_PLAYING && hotkey < FX_KEYS_MAX_VIRTUAL) {
7125     if (prefs->allow_easing) {
7126       // if it's a user key and the plugin supports easing out, we'll do that instead
7127       weed_plant_t *gui = weed_instance_get_gui(instance, FALSE);
7128       if (!weed_plant_has_leaf(gui, WEED_LEAF_EASE_OUT)) {
7129         uint64_t new_rte = GU641 << (hotkey);
7130         if (mainw->rte & new_rte) {
7131           if ((easing = weed_get_int_value(gui, WEED_LEAF_EASE_OUT_FRAMES, NULL))) {
7132             int myease = cfile->pb_fps * 2.;
7133             if (easing < myease) {
7134               weed_set_int_value(gui, WEED_LEAF_EASE_OUT, myease);
7135               weed_set_int_value(instance, WEED_LEAF_HOST_EASE_OUT_COUNT, myease);
7136               weed_instance_unref(instance);
7137               return FALSE;
7138 	      // *INDENT-OFF*
7139 	    }}}}}}
7140     // *INDENT-ON*
7141   mainw->blend_palette = WEED_PALETTE_END;
7142 
7143   // disable param recording, in case the instance is still attached to a param window
7144   weed_set_boolean_value(instance, WEED_LEAF_HOST_NORECORD, WEED_TRUE);
7145 
7146   num_in_chans = enabled_in_channels(instance, FALSE);
7147 
7148   gui = weed_instance_get_gui(instance, FALSE);
7149   if (gui && weed_get_int_value(gui, WEED_LEAF_EASE_OUT, NULL)) {
7150     weed_leaf_delete(gui, WEED_LEAF_EASE_OUT);
7151   }
7152   weed_leaf_delete(instance, WEED_LEAF_HOST_EASE_OUT_COUNT);
7153   weed_leaf_delete(instance, WEED_LEAF_AUTO_EASING);
7154 
7155   // handle compound fx
7156   last_inst = instance;
7157   while (weed_plant_has_leaf(last_inst, WEED_LEAF_HOST_NEXT_INSTANCE)) last_inst = weed_get_plantptr_value(last_inst,
7158         WEED_LEAF_HOST_NEXT_INSTANCE, &error);
7159   num_out_chans = enabled_out_channels(last_inst, FALSE);
7160 
7161   if (hotkey + 1 == mainw->agen_key) is_audio_gen = TRUE;
7162   else if (num_in_chans == 0) is_video_gen = TRUE;
7163 
7164   filter = weed_instance_get_filter(instance, TRUE);
7165   if ((is_video_gen || all_ins_alpha(filter, TRUE)) && num_out_chans > 0 && !all_outs_alpha(filter, TRUE)) {
7166     weed_instance_unref(instance); // remove ref from weed instance obtain
7167     /////////////////////////////////// deinit a (video) generator
7168     if (LIVES_IS_PLAYING && mainw->whentostop == STOP_ON_VID_END && (hotkey != bg_generator_key)) {
7169       mainw->cancelled = CANCEL_GENERATOR_END; // will be unreffed on pb end
7170     } else {
7171       weed_generator_end(instance); // removes 1 ref
7172     }
7173     return TRUE;
7174   }
7175 
7176   if (is_audio_gen) {
7177     // is audio generator
7178     // wait for current processing to finish
7179     mainw->agen_key = 0;
7180     mainw->agen_samps_count = 0;
7181 
7182     // for external audio, switch back to reading
7183 
7184     if (mainw->playing_file > 0 && prefs->audio_src == AUDIO_SRC_EXT) {
7185 #ifdef ENABLE_JACK
7186       if (prefs->audio_player == AUD_PLAYER_JACK) {
7187         if (!mainw->jackd_read || !mainw->jackd_read->in_use) {
7188           mainw->jackd->in_use = FALSE; // deactivate writer
7189           jack_rec_audio_to_clip(-1, 0, RECA_MONITOR); //activate reader
7190           jack_time_reset(mainw->jackd_read, lives_jack_get_time(mainw->jackd)); // ensure time continues monotonically
7191           if (mainw->record) mainw->jackd_read->playing_file = mainw->ascrap_file; // if recording, continue to write to ascrap file
7192           mainw->jackd_read->is_paused = FALSE;
7193           mainw->jackd_read->in_use = TRUE;
7194           if (mainw->jackd) {
7195             mainw->jackd->playing_file = -1;
7196           }
7197         }
7198       }
7199 #endif
7200 #ifdef HAVE_PULSE_AUDIO
7201       if (prefs->audio_player == AUD_PLAYER_PULSE) {
7202         if (!mainw->pulsed_read || !mainw->pulsed_read->in_use) {
7203           if (mainw->pulsed) mainw->pulsed->in_use = FALSE; // deactivate writer
7204           pulse_rec_audio_to_clip(-1, 0, RECA_MONITOR); //activate reader
7205           if (mainw->pulsed) {
7206             ticks_t audio_ticks = lives_pulse_get_time(mainw->pulsed_read);
7207             if (audio_ticks == -1) {
7208               mainw->cancelled = handle_audio_timeout();
7209               weed_instance_unref(instance); // remove ref from weed instance obtain
7210               return TRUE;
7211             }
7212             pa_time_reset(mainw->pulsed_read, -audio_ticks); // ensure time continues monotonically
7213             pulse_driver_uncork(mainw->pulsed_read);
7214             if (mainw->pulsed) {
7215               pulse_driver_cork(mainw->pulsed);
7216               mainw->pulsed->playing_file = -1;
7217             }
7218           }
7219           if (mainw->record) mainw->pulsed_read->playing_file = mainw->ascrap_file; // if recording, continue to write to ascrap file
7220           mainw->pulsed_read->is_paused = FALSE;
7221           mainw->pulsed_read->in_use = TRUE;
7222         }
7223       }
7224 #endif
7225     } else if (mainw->playing_file > 0) {
7226       // for internal, continue where we should
7227       if (is_realtime_aplayer(prefs->audio_player)) {
7228         if (prefs->audio_opts & AUDIO_OPTS_FOLLOW_CLIPS) switch_audio_clip(mainw->playing_file, TRUE);
7229         else {
7230           if (mainw->pre_src_audio_file == -1) {
7231             // audio doesn't follow clip switches and we were playing this...
7232             mainw->cancelled = CANCEL_AUD_END;
7233           } else {
7234 #ifdef HAVE_PULSE_AUDIO
7235             if (prefs->audio_player == AUD_PLAYER_PULSE) {
7236               if (mainw->pulsed) mainw->pulsed->playing_file = mainw->pre_src_audio_file;
7237               if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO)) {
7238                 pulse_get_rec_avals(mainw->pulsed);
7239               }
7240             }
7241 #endif
7242 #ifdef ENABLE_JACK
7243             if (prefs->audio_player == AUD_PLAYER_JACK) {
7244               if (mainw->jackd) mainw->jackd->playing_file = mainw->pre_src_audio_file;
7245               if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO)) {
7246                 jack_get_rec_avals(mainw->jackd);
7247               }
7248             }
7249 #endif
7250 	    // *INDENT-OFF*
7251           }}}}}
7252   // *INDENT-ON*
7253 
7254   if (!filter_mutex_trylock(hotkey)) {
7255     // should fail, but just in case
7256     needs_unlock = hotkey;
7257   }
7258   key_to_instance[hotkey][key_modes[hotkey]] = NULL;
7259   if (needs_unlock != -1) {
7260     filter_mutex_unlock(needs_unlock);
7261     needs_unlock = -1;
7262   }
7263 
7264   if (hotkey < FX_KEYS_MAX_VIRTUAL) {
7265     if (mainw->whentostop == STOP_ON_VID_END && mainw->current_file > -1 &&
7266         (cfile->frames == 0 || (mainw->loop && cfile->achans > 0 && !mainw->is_rendering && (mainw->audio_end / cfile->fps)
7267                                 < MAX(cfile->laudio_time, cfile->raudio_time)))) mainw->whentostop = STOP_ON_AUD_END;
7268 
7269     if (num_in_chans == 2) {
7270       was_transition = TRUE;
7271       mainw->num_tr_applied--;
7272       if (mainw->num_tr_applied == 0) {
7273         if (mainw->ce_thumbs) ce_thumbs_liberate_clip_area_register(SCREEN_AREA_FOREGROUND);
7274         if (mainw->active_sa_clips == SCREEN_AREA_BACKGROUND) {
7275           mainw->active_sa_clips = SCREEN_AREA_FOREGROUND;
7276 	  // *INDENT-OFF*
7277         }}}}
7278   // *INDENT-ON*
7279   inst = instance;
7280 
7281 deinit3:
7282 
7283   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE))
7284     next_inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE,
7285                                         &error);
7286   else next_inst = NULL;
7287 
7288   weed_call_deinit_func(inst);
7289   weed_instance_unref(inst); // remove ref from weed_instance_obtain
7290   weed_instance_unref(inst);  // free if no other refs
7291 
7292   if (next_inst) {
7293     // handle compound fx
7294     inst = next_inst;
7295     weed_instance_ref(inst); // proxy for weed_instance_obtain
7296     goto deinit3;
7297   }
7298 
7299   // if the param window is already open, show any reinits now
7300   if (fx_dialog[1] && fx_dialog[1]->key == hotkey && fx_dialog[1]->mode == key_modes[hotkey]) {
7301     update_widget_vis(NULL, hotkey, key_modes[hotkey]); // redraw our paramwindow
7302   }
7303 
7304   if (was_transition && !is_modeswitch) {
7305     if (mainw->num_tr_applied < 1) {
7306       if (CURRENT_CLIP_IS_VALID && mainw->effort > 0) {
7307         reset_effort();
7308       }
7309       if (bg_gen_to_start != -1) bg_gen_to_start = -1;
7310       if (mainw->blend_file != -1 && mainw->blend_file != mainw->current_file && mainw->files[mainw->blend_file] &&
7311           mainw->files[mainw->blend_file]->clip_type == CLIP_TYPE_GENERATOR) {
7312         // all transitions off, so end the bg generator
7313         // lock the filter mutex if it's not locked
7314         if (!filter_mutex_trylock(bg_generator_key)) {
7315           needs_unlock = bg_generator_key; // will get reset to -1
7316         }
7317         weed_deinit_effect(bg_generator_key);
7318         if (needs_unlock != -1) {
7319           filter_mutex_unlock(needs_unlock);
7320           needs_unlock = -1;
7321         }
7322       }
7323       mainw->blend_file = -1;
7324     }
7325   }
7326   if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && init_events[hotkey] &&
7327       (prefs->rec_opts & REC_EFFECTS) && num_in_chans > 0) {
7328     uint64_t rteval, new_rte;
7329     //ticks_t actual_ticks = mainw->clock_ticks;//lives_get_current_playback_ticks(mainw->origsecs, mainw->orignsecs, NULL);
7330     ticks_t actual_ticks = mainw->startticks; ///< use the "thoretical" time
7331     pthread_mutex_lock(&mainw->event_list_mutex);
7332     mainw->event_list = append_filter_deinit_event(mainw->event_list, actual_ticks, init_events[hotkey], pchains[hotkey]);
7333     init_events[hotkey] = NULL;
7334     if (pchains[hotkey]) lives_free(pchains[hotkey]);
7335     pchains[hotkey] = NULL;
7336     rteval = mainw->rte;
7337     new_rte = GU641 << (hotkey);
7338     if (rteval & new_rte) {
7339       rteval ^= new_rte;
7340       if (rte_window) rtew_set_keych(hotkey, FALSE);
7341       if (mainw->ce_thumbs) ce_thumbs_set_keych(hotkey, FALSE);
7342     }
7343     create_filter_map(rteval); // we create filter_map event_t * array with ordered effects
7344     mainw->event_list = append_filter_map_event(mainw->event_list, actual_ticks, filter_map);
7345     pthread_mutex_unlock(&mainw->event_list_mutex);
7346   }
7347   return TRUE;
7348 }
7349 
7350 
7351 /**
7352    @brief switch off effects after render preview
7353    during rendering/render preview, we use the "keys" FX_KEYS_MAX_VIRTUAL -> FX_KEYS_MAX
7354    here we deinit all active ones, similar to weed_deinit_all, but we use higher keys
7355 
7356    @see weed_deinit_all()
7357 */
deinit_render_effects(void)7358 void deinit_render_effects(void) {
7359   register int i;
7360 
7361   for (i = FX_KEYS_MAX_VIRTUAL; i < FX_KEYS_MAX; i++) {
7362     if (key_to_instance[i][0]) {
7363       // no mutex needed since we are rendering
7364       weed_deinit_effect(i);
7365       if (mainw->multitrack && mainw->multitrack->is_rendering && pchains[i]) {
7366         lives_free(pchains[i]);
7367         pchains[i] = NULL;
7368 	// *INDENT-OFF*
7369       }}}
7370   // *INDENT-ON*
7371 }
7372 
7373 
7374 /**
7375    @brief switch off effects in easing out state after playback ends
7376    during playback, some effects don't deinit right awy, instead they ease out
7377    if plyback ends while they are in the easing out state they won't get deinited, so we need
7378    to take care of that
7379    @see weed_deinit_all()
7380 */
deinit_easing_effects(void)7381 void deinit_easing_effects(void) {
7382   weed_plant_t *instance;
7383   register int i;
7384 
7385   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
7386     if ((instance = key_to_instance[i][key_modes[i]]) != NULL) {
7387       weed_plant_t *gui = weed_instance_get_gui(instance, FALSE);
7388       if (weed_plant_has_leaf(gui, WEED_LEAF_EASE_OUT)) {
7389         // no mutex needed since we are rendering. and since we arent playing it will get deinited now
7390         weed_deinit_effect(i);
7391         /// if recording, the deinit_event won't be recorded, since we are not playing now,
7392         /// this will be handled in deal_with_render_choice() so it need not concern us
7393 	  // *INDENT-OFF*
7394 	}}}
7395   // *INDENT-ON*
7396 }
7397 
7398 
7399 /**
7400    @brief deinit all effects (except generators* during playback)
7401    this is called on ctrl-0 or on shutdown
7402      background generators will be killed because their transition will be deinited
7403 */
weed_deinit_all(boolean shutdown)7404 void weed_deinit_all(boolean shutdown) {
7405   int i;
7406   weed_plant_t *instance;
7407 
7408   mainw->osc_block = TRUE;
7409   if (rte_window) {
7410     rtew_set_keygr(-1);
7411   }
7412 
7413   mainw->rte_keys = -1;
7414   mainw->last_grabbable_effect = -1;
7415   if (!LIVES_IS_PLAYING) bg_gen_to_start = bg_generator_key = bg_generator_mode = -1;
7416 
7417   for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
7418     if (!shutdown) {
7419       // maintain braces because of #DEBUG_FILTER_MUTEXES
7420       if (LIVES_IS_PLAYING && !shutdown && i >= FX_KEYS_PHYSICAL) {
7421         // meta-physical  keys only shutdown through the interface or via easter-egg keys...
7422         mainw->osc_block = FALSE;
7423         return;
7424       }
7425       filter_mutex_lock(i);
7426     } else {
7427       // on shutdown, another thread might be deadlocked on this
7428       // so we will unlock it after
7429       filter_mutex_trylock(i);
7430     }
7431     if ((rte_key_is_enabled(1 + i))) {
7432       if ((instance = weed_instance_obtain(i, key_modes[i])) != NULL) {
7433         if (shutdown || !LIVES_IS_PLAYING || mainw->current_file == -1 || cfile->ext_src != instance) {
7434           if (rte_window) rtew_set_keych(i, FALSE);
7435           if (mainw->ce_thumbs) ce_thumbs_set_keych(i, FALSE);
7436           weed_deinit_effect(i);
7437           pthread_mutex_lock(&mainw->event_list_mutex);
7438           mainw->rte ^= (GU641 << i);
7439           pthread_mutex_unlock(&mainw->event_list_mutex);
7440         } else weed_instance_unref(instance);
7441       }
7442     }
7443     filter_mutex_unlock(i);
7444   }
7445 
7446   mainw->osc_block = FALSE;
7447 }
7448 
7449 
7450 /**
7451    @brief registration fn. for video effects with audio input channels
7452    during playback there is the option of buffering audio sent to the soundcard
7453    if a video effect requires audio it can register itself here, and the audio buffer will be filled and
7454    only refreshed after all clients have read from it. Once this has happened, the read / write buffers are swapped for the
7455    following cycle. This ensures that no client will miss audio samples, at the cost of some minor latency.
7456    Purely audio filters are run directly during the audio cycle or by the audio caching thread.
7457 */
register_audio_channels(int nchannels)7458 static int register_audio_channels(int nchannels) {
7459   if (!is_realtime_aplayer(prefs->audio_player)) {
7460     mainw->afbuffer_clients = 0;
7461     return -1;
7462   }
7463   if (nchannels <= 0) return mainw->afbuffer_clients;
7464   pthread_mutex_lock(&mainw->abuf_frame_mutex);
7465   if (mainw->afbuffer_clients == 0) {
7466     init_audio_frame_buffers(prefs->audio_player);
7467     mainw->afbuffer_clients_read = 0;
7468   }
7469   mainw->afbuffer_clients += nchannels;
7470   pthread_mutex_unlock(&mainw->abuf_frame_mutex);
7471   return mainw->afbuffer_clients;
7472 }
7473 
7474 
unregister_audio_channels(int nchannels)7475 static int unregister_audio_channels(int nchannels) {
7476   if (!mainw->audio_frame_buffer) {
7477     mainw->afbuffer_clients = 0;
7478     return -1;
7479   }
7480 
7481   mainw->afbuffer_clients -= nchannels;
7482   if (mainw->afbuffer_clients <= 0) {
7483     int i;
7484     // lock out the audio thread
7485     pthread_mutex_lock(&mainw->abuf_frame_mutex);
7486     for (i = 0; i < 2; i++) {
7487       if (mainw->afb[i]) {
7488         free_audio_frame_buffer(mainw->afb[i]);
7489         lives_free(mainw->afb[i]);
7490         mainw->afb[i] = NULL;
7491       }
7492     }
7493     mainw->audio_frame_buffer = NULL;
7494     pthread_mutex_unlock(&mainw->abuf_frame_mutex);
7495   }
7496   return mainw->afbuffer_clients;
7497 }
7498 
7499 
fill_audio_channel(weed_plant_t * filter,weed_plant_t * achan)7500 static boolean fill_audio_channel(weed_plant_t *filter, weed_plant_t *achan) {
7501   // this is for filter instances with mixed audio / video inputs/outputs
7502   // uneffected audio is buffered by the audio thread; here we copy / convert it to a video effect's audio channel
7503   // purely audio filters run in the audio thread
7504 
7505   static lives_audio_buf_t *audbuf;
7506 
7507   if (achan) {
7508     weed_set_int_value(achan, WEED_LEAF_AUDIO_DATA_LENGTH, 0);
7509     weed_set_voidptr_value(achan, WEED_LEAF_AUDIO_DATA, NULL);
7510   }
7511 
7512   if (!mainw->audio_frame_buffer || mainw->audio_frame_buffer->samples_filled <= 0 || mainw->afbuffer_clients == 0) {
7513     // no audio has been buffered
7514     return FALSE;
7515   }
7516 
7517   // lock the buffers
7518 
7519   if (mainw->afbuffer_clients_read == 0) {
7520     /// when the first client reads, we grab the audio frame buffer and swap the other for writing
7521     // cast away the (volatile)
7522     pthread_mutex_lock(&mainw->abuf_frame_mutex);
7523     audbuf = (lives_audio_buf_t *)mainw->audio_frame_buffer;
7524     if (audbuf == mainw->afb[0]) mainw->audio_frame_buffer = mainw->afb[1];
7525     else mainw->audio_frame_buffer = mainw->afb[0];
7526     pthread_mutex_unlock(&mainw->abuf_frame_mutex);
7527   }
7528 
7529   // push read buffer to channel
7530   if (achan && audbuf) {
7531     // convert audio to format requested, and copy it to the audio channel data
7532     push_audio_to_channel(filter, achan, audbuf);
7533   }
7534 
7535   if (++mainw->afbuffer_clients_read >= mainw->afbuffer_clients) {
7536     // all clients have read the data, now we can free it
7537     free_audio_frame_buffer(audbuf);
7538     mainw->afbuffer_clients_read = 0;
7539   }
7540 
7541   return TRUE;
7542 }
7543 
7544 
check_filter_chain_palettes(boolean is_bg,int * palette_list,int npals)7545 int check_filter_chain_palettes(boolean is_bg, int *palette_list, int npals) {
7546   register int i;
7547   int palette = WEED_PALETTE_END;
7548 
7549   if (mainw->rte) {
7550     for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
7551       if (palette != WEED_PALETTE_END) break;
7552       if (rte_key_is_enabled(1 + i)) {
7553         weed_plant_t *instance;
7554         if (i == fg_generator_key) continue;
7555         if ((instance = weed_instance_obtain(i, key_modes[i])) != NULL) {
7556           if (is_bg && enabled_in_channels(instance, FALSE) < 2) continue;
7557           weed_plant_t *filter = weed_instance_get_filter(instance, TRUE);
7558           weed_plant_t *channel = get_enabled_channel(instance, (is_bg ? 1 : 0), TRUE);
7559           if (channel) {
7560             int nvals;
7561             weed_plant_t *chantmpl = weed_channel_get_template(channel);
7562             int *plist = weed_chantmpl_get_palette_list(filter, chantmpl, &nvals);
7563             for (int j = 0; j < nvals; j++) {
7564               if (plist[j] == WEED_PALETTE_END) break;
7565               else for (int k = 0; k < npals; k++) {
7566                   if (palette_list[k] == WEED_PALETTE_END) break;
7567                   if (palette_list[k] == plist[j]) {
7568                     palette = plist[j];
7569                     break;
7570 		    // *INDENT-OFF*
7571 		  }}
7572 	      if (palette != WEED_PALETTE_END) break;
7573 	    }
7574             lives_free(plist);
7575           }
7576 	  weed_instance_unref(instance);
7577 	}}}
7578     // *INDENT-ON*
7579 
7580     if (palette == WEED_PALETTE_END) {
7581       if (mainw->ext_playback && (mainw->vpp->capabilities & VPP_LOCAL_DISPLAY)) {
7582         for (i = 0; i < npals; i++) {
7583           if (palette_list[i] == mainw->vpp->palette) {
7584             palette = palette_list[i];
7585             break;
7586 	    // *INDENT-OFF*
7587 	  }}}}}
7588   // *INDENT-ON*
7589 
7590   if (palette == WEED_PALETTE_END) {
7591     for (i = 0; i < npals; i++) {
7592       if (weed_palette_is_pixbuf_palette(palette_list[i])) break;
7593     }
7594   }
7595   if (palette == WEED_PALETTE_END) {
7596     for (i = 0; i < npals; i++) {
7597       if (weed_palette_is_rgb(palette_list[i])) break;
7598     }
7599   }
7600   if (palette == WEED_PALETTE_END) palette = palette_list[0];
7601   return palette;
7602 }
7603 
7604 /////////////////////
7605 // special handling for generators (sources)
7606 
weed_layer_create_from_generator(weed_plant_t * inst,weed_timecode_t tc,int clipno)7607 weed_plant_t *weed_layer_create_from_generator(weed_plant_t *inst, weed_timecode_t tc, int clipno) {
7608   weed_plant_t *channel, **out_channels;
7609   weed_plant_t *filter;
7610   weed_plant_t *chantmpl;
7611   weed_plant_t *achan;
7612   weed_process_f process_func;
7613   int *palette_list;
7614   int *rowstrides;
7615   lives_filter_error_t retval;
7616   weed_error_t ret;
7617   int npals;
7618   int num_channels;
7619   int npl, npl2;
7620   int palette, opalette;
7621   int filter_flags = 0, channel_flags;
7622   int num_in_alpha = 0;
7623   int width, height, xwidth, xheight;
7624   int reinits = 0;
7625 
7626   int i;
7627 
7628   boolean did_thread = FALSE;
7629   boolean needs_reinit = FALSE;
7630   boolean can_change = FALSE;
7631   boolean is_bg = TRUE;
7632   char *cwd;
7633 
7634   if (!inst) return NULL;
7635 
7636   weed_instance_ref(inst);
7637 
7638   out_channels = weed_get_plantptr_array_counted(inst, WEED_LEAF_OUT_CHANNELS, &num_channels);
7639   if (num_channels  == 0) {
7640     weed_instance_unref(inst);
7641     return NULL;
7642   }
7643 
7644   if ((channel = get_enabled_channel(inst, 0, FALSE)) == NULL) {
7645     lives_free(out_channels);
7646     weed_instance_unref(inst);
7647     return NULL;
7648   }
7649 
7650   filter = weed_instance_get_filter(inst, FALSE);
7651   filter_flags = weed_filter_get_flags(filter);
7652   chantmpl = weed_channel_get_template(channel);
7653   palette_list = weed_chantmpl_get_palette_list(filter, chantmpl, &npals);
7654   palette = WEED_PALETTE_END;
7655 
7656   if (clipno == mainw->current_file) is_bg = FALSE;
7657 
7658   /*
7659     - just as with normal filters,
7660     - if the channel template flags have WEED_FILTER_REINIT_ON_PALETTE_CHANGE, then we want to minimise palette changes,
7661     in this case because reinit may disrupt the output flow from the generator, causing unnecessary visual changes.
7662     thus unless forced to, we will leave the palette as is. However as with filters,
7663     There are two points where we can voluntariily alter the palette:
7664     when the instance has just been inited (since it would be reset anyway)
7665     and if we are going to reinit anyway due to some other factor (e.g size change).
7666 
7667     when setting the palette we will consider the following:
7668     - if there are no filters active then look at the player palette. If the player can change palettes then find the best pair.
7669     else
7670     - if the gen. is fg,  check the filters to be applied, If 1st filter has reinit on pal. change then try to match its palette
7671     - otherwise check the longest running palette, ie. intersection of gen. palette(s), filter_n  palettes, filter_n+1 pal. list untiil
7672     either the intersection of our remaing palettes with filter palettes is the empty set. or we pass all palettes,
7673     then take intersection with player. Any filters with reinit on pal. change, we first try the current palette, if that doesnt work then
7674     we check its full palette list as we will be forced to reinit it.
7675     - if it is bg we will use mainw->blend_palette
7676     TODO: we should finde the transition where the gen. joins, and work backwards and forwards from there
7677 
7678 
7679     IF the template has WEED_FILTER_REINIT_ON_ROWSTRIDES_CHANGE or WEED_FILTER_REINIT_ON_SIZE_CHANGE
7680     then similar rules apply for those values
7681   */
7682 
7683   /// if we have a choice of palettes, try to match with the first filter, if not, with the player
7684   // unless it says reinit on palette change. Then as with normal filters we only change after an init / reinit
7685 
7686 matchvals:
7687 
7688   if (needs_reinit || weed_get_boolean_value(inst, WEED_LEAF_HOST_UNUSED, NULL) == WEED_TRUE)
7689     can_change = TRUE;
7690 
7691   needs_reinit = FALSE;
7692 
7693   channel = get_enabled_channel(inst, 0, FALSE);
7694   chantmpl = weed_channel_get_template(channel);
7695   channel_flags = weed_chantmpl_get_flags(chantmpl);
7696 
7697   opalette = weed_channel_get_palette(channel);
7698   if (can_change || !((channel_flags & WEED_CHANNEL_REINIT_ON_PALETTE_CHANGE))) {
7699     palette = check_filter_chain_palettes(is_bg, palette_list, npals);
7700     if (palette != WEED_PALETTE_END) {
7701       if (palette != opalette && !((channel_flags & WEED_CHANNEL_REINIT_ON_ROWSTRIDES_CHANGE) && pixel_size(palette)
7702                                    != pixel_size(opalette))) {
7703         weed_channel_set_palette(channel, palette);
7704       }
7705     }
7706   }
7707 
7708   if (weed_plant_has_leaf(inst, WEED_LEAF_IN_CHANNELS)) {
7709     int num_inc;
7710     weed_plant_t **in_channels = weed_get_plantptr_array_counted(inst, WEED_LEAF_IN_CHANNELS, &num_inc);
7711     for (i = 0; i < num_inc; i++) {
7712       if (weed_palette_is_alpha(weed_channel_get_palette(in_channels[i])) &&
7713           weed_get_boolean_value(in_channels[i], WEED_LEAF_DISABLED, NULL) == WEED_FALSE) {
7714         num_in_alpha++;
7715       }
7716       lives_freep((void **)&in_channels);
7717     }
7718   }
7719 
7720   if (num_in_alpha > 0) {
7721     // if we have mandatory alpha ins, make sure they are filled
7722     retval = check_cconx(inst, num_in_alpha, &needs_reinit);
7723     if (retval != FILTER_SUCCESS) {
7724       lives_free(out_channels);
7725       weed_instance_unref(inst);
7726       return channel;
7727     }
7728   }
7729 
7730   xwidth = width = weed_channel_get_width(channel);
7731   //cpalette = weed_channel_get_palette(channel);
7732   //xwidth = width /= weed_palette_get_pixels_per_macropixel(cpalette); // convert width to channel macropixels (????)
7733   xheight = height = weed_channel_get_height(channel);
7734 
7735   if (can_change || (!(channel_flags & WEED_CHANNEL_REINIT_ON_ROWSTRIDES_CHANGE) && !(channel_flags
7736                      & WEED_CHANNEL_REINIT_ON_SIZE_CHANGE))) {
7737     // if it's in the bg, and letterboxing, set size to the fg clip
7738     // or if it's fg or bg and we are playing high quality
7739     if (mainw->num_tr_applied > 0 && !num_in_alpha) {
7740       if (IS_VALID_CLIP(mainw->blend_file) && mainw->blend_file != mainw->playing_file
7741           && mainw->files[mainw->blend_file]->ext_src == inst) {
7742         boolean is_lbox = FALSE;
7743         int lb_width = mainw->files[mainw->playing_file]->hsize;
7744         int lb_height = mainw->files[mainw->playing_file]->vsize;
7745         if ((mainw->multitrack && !mainw->multitrack->is_rendering && prefs->letterbox_mt)
7746             || (!mainw->multitrack && prefs->letterbox && (!mainw->is_rendering || mainw->preview_rendering))) {
7747           boolean can_resize = FALSE;
7748           int opwidth, opheight;
7749           get_player_size(&opwidth, &opheight);
7750           if (mainw->ext_playback && (mainw->vpp->capabilities & VPP_CAN_RESIZE) != 0) can_resize = TRUE;
7751           get_letterbox_sizes(&opwidth, &opheight, &lb_width, &lb_height, can_resize);
7752           is_lbox = TRUE;
7753         }
7754         if (is_lbox || (prefs->pb_quality == PB_QUALITY_HIGH && (lb_width > width || lb_height > height))) {
7755           xwidth = lb_width;
7756           xheight = lb_height;
7757         }
7758       }
7759     } else {
7760       if (IS_VALID_CLIP(mainw->blend_file) && mainw->blend_file != mainw->playing_file
7761           && prefs->pb_quality == PB_QUALITY_HIGH && (mainw->files[mainw->blend_file]->hsize > width
7762               || mainw->files[mainw->blend_file]->vsize > height)) {
7763         xwidth = mainw->files[mainw->blend_file]->hsize;
7764         xheight = mainw->files[mainw->blend_file]->vsize;
7765       }
7766     }
7767   }
7768 
7769   if (xwidth * xheight == 0) {
7770     if (weed_plant_has_leaf(chantmpl, WEED_LEAF_HOST_WIDTH)) {
7771       xwidth = weed_get_int_value(chantmpl, WEED_LEAF_HOST_WIDTH, NULL);
7772     } else xwidth = DEF_GEN_WIDTH;
7773     if (weed_plant_has_leaf(chantmpl, WEED_LEAF_HOST_HEIGHT)) {
7774       xheight = weed_get_int_value(chantmpl, WEED_LEAF_HOST_HEIGHT, NULL);
7775     } else xheight = DEF_GEN_HEIGHT;
7776   }
7777 
7778   rowstrides = weed_channel_get_rowstrides(channel, &npl);
7779 
7780   if (weed_plant_has_leaf(filter, WEED_LEAF_ALIGNMENT_HINT)) {
7781     int rowstride_alignment_hint = weed_get_int_value(filter, WEED_LEAF_ALIGNMENT_HINT, NULL);
7782     if (rowstride_alignment_hint > ALIGN_DEF) {
7783       THREADVAR(rowstride_alignment_hint) = rowstride_alignment_hint;
7784     }
7785   }
7786 
7787   /// store original values (width / height)
7788   for (i = 0; (channel = get_enabled_channel(inst, i, FALSE)); i++) {
7789     if (xwidth != weed_channel_get_width(channel) ||
7790         xheight != weed_channel_get_height(channel)) {
7791       int nplanes;
7792       void **pd = weed_channel_get_pixel_data_planar(channel, &nplanes);
7793       for (int j = 0; j < nplanes; j++) lives_free(pd[j]);
7794       lives_free(pd);
7795       weed_set_voidptr_value(channel, WEED_LEAF_PIXEL_DATA, NULL);
7796       set_channel_size(filter, channel, xwidth, xheight);
7797     }
7798   }
7799 
7800   channel = get_enabled_channel(inst, 0, FALSE);
7801 
7802   if (!create_empty_pixel_data(channel, FALSE, TRUE)) return NULL;
7803 
7804   /// store original values (width / height)
7805   xwidth = width;
7806   xheight = height;
7807 
7808   width = weed_channel_get_width(channel);
7809   //cpalette = weed_channel_get_palette(channel);
7810   //width /= weed_palette_get_pixels_per_macropixel(cpalette); // convert width to channel macropixels
7811   height = weed_channel_get_height(channel);
7812 
7813   if (xwidth != width || xheight != height) {
7814     if (channel_flags & WEED_CHANNEL_REINIT_ON_SIZE_CHANGE) {
7815       needs_reinit = TRUE;
7816     }
7817   }
7818 
7819   if (!needs_reinit && (channel_flags & WEED_CHANNEL_REINIT_ON_ROWSTRIDES_CHANGE)) {
7820     int *rowstrides2 = weed_channel_get_rowstrides(channel, &npl2);
7821     if (rowstrides_differ(npl, rowstrides, npl2, rowstrides2))
7822       needs_reinit = TRUE;
7823     lives_free(rowstrides2);
7824   }
7825   lives_free(rowstrides);
7826 
7827   if (filter_flags & WEED_FILTER_PREF_LINEAR_GAMMA)
7828     weed_channel_set_gamma_type(channel, WEED_GAMMA_LINEAR);
7829   else
7830     weed_channel_set_gamma_type(channel, cfile->gamma_type);
7831 
7832   if (needs_reinit) {
7833     if ((retval = weed_reinit_effect(inst, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
7834       lives_free(out_channels);
7835       weed_instance_unref(inst);
7836       return NULL;
7837     }
7838     goto matchvals;
7839   }
7840 
7841   if (i == 0 && mainw->current_file == mainw->playing_file) {
7842     cfile->hsize = width;
7843     cfile->vsize = height;
7844     set_main_title(cfile->file_name, 0);
7845   }
7846 
7847   // if we have an optional audio channel, we can push audio to it
7848   if ((achan = get_enabled_audio_channel(inst, 0, TRUE)) != NULL) {
7849     fill_audio_channel(filter, achan);
7850   }
7851 
7852   if (prefs->pb_quality == PB_QUALITY_LOW && mainw->inst_fps > 0. && mainw->inst_fps
7853       < weed_get_double_value(inst, WEED_LEAF_TARGET_FPS, NULL))
7854     weed_set_double_value(inst, WEED_LEAF_TARGET_FPS, mainw->inst_fps);
7855   else {
7856     if (weed_plant_has_leaf(filter, WEED_LEAF_HOST_FPS))
7857       weed_leaf_copy(inst, WEED_LEAF_TARGET_FPS, filter, WEED_LEAF_HOST_FPS);
7858     else if (weed_plant_has_leaf(filter, WEED_LEAF_PREFERRED_FPS))
7859       weed_leaf_copy(inst, WEED_LEAF_TARGET_FPS, filter, WEED_LEAF_PREFERRED_FPS);
7860   }
7861 
7862   if (CURRENT_CLIP_IS_VALID) weed_set_double_value(inst, WEED_LEAF_FPS, mainw->inst_fps);
7863 
7864   cwd = cd_to_plugin_dir(filter);
7865 
7866 procfunc1:
7867   // cannot lock the filter here as we may be multithreading
7868   if (filter_flags & WEED_FILTER_HINT_MAY_THREAD) {
7869     retval = process_func_threaded(inst, tc);
7870     if (retval != FILTER_ERROR_DONT_THREAD) did_thread = TRUE;
7871   }
7872   if (!did_thread) {
7873     // normal single threaded version
7874     process_func = (weed_process_f)weed_get_funcptr_value(filter, WEED_LEAF_PROCESS_FUNC, NULL);
7875     if (process_func)
7876       ret = (*process_func)(inst, tc);
7877   }
7878   lives_free(out_channels);
7879 
7880   //if (weed_palette_has_alpha(palette)) lives_layer_set_opaque(channel);
7881 
7882   if (achan) {
7883     int nachans;
7884     float **abuf = weed_channel_get_audio_data(achan, &nachans);
7885     for (i = 0; i < nachans; i++) lives_freep((void **)&abuf[i]);
7886     lives_freep((void **)&abuf);
7887   }
7888 
7889   if (ret == WEED_ERROR_REINIT_NEEDED) {
7890     if (reinits == 1) {
7891       weed_instance_unref(inst);
7892       return channel;
7893     }
7894     if ((retval = weed_reinit_effect(inst, FALSE)) == FILTER_ERROR_COULD_NOT_REINIT) {
7895       weed_instance_unref(inst);
7896       return channel;
7897     }
7898     reinits = 1;
7899     goto procfunc1;
7900   }
7901 
7902   weed_instance_unref(inst);
7903 
7904   lives_chdir(cwd, FALSE);
7905   lives_free(cwd);
7906 
7907   return channel;
7908 }
7909 
7910 
weed_generator_start(weed_plant_t * inst,int key)7911 int weed_generator_start(weed_plant_t *inst, int key) {
7912   // key here is zero based
7913   // make an "ephemeral clip"
7914 
7915   // cf. yuv4mpeg.c
7916   // start "playing" but receive frames from a plugin
7917 
7918   // filter_mutex MUST be locked, on FALSE return is unlocked
7919 
7920   weed_plant_t **out_channels, *channel, *filter, *achan;
7921   char *filter_name;
7922   int error, num_channels;
7923   int old_file = mainw->current_file, blend_file = mainw->blend_file;
7924   int palette;
7925 
7926   // create a virtual clip
7927   int new_file = 0;
7928   boolean is_bg = FALSE;
7929 
7930   //weed_instance_ref(inst);
7931   //filter_mutex_lock(key);
7932 
7933   if (LIVES_IS_PLAYING) mainw->ignore_clipswitch = TRUE;
7934 
7935   if (CURRENT_CLIP_IS_VALID && cfile->clip_type == CLIP_TYPE_GENERATOR && mainw->num_tr_applied == 0) {
7936     close_current_file(0);
7937     old_file = -1;
7938   }
7939 
7940   if (mainw->is_processing && !mainw->preview) {
7941     filter_mutex_unlock(key);
7942     return 1;
7943   }
7944 
7945   if ((mainw->preview || (CURRENT_CLIP_IS_VALID && cfile->opening)) &&
7946       (mainw->num_tr_applied == 0 || mainw->blend_file == -1 || mainw->blend_file == mainw->current_file)) {
7947     filter_mutex_unlock(key);
7948     return 2;
7949   }
7950 
7951   if (!LIVES_IS_PLAYING) mainw->pre_src_file = mainw->current_file;
7952 
7953   if (old_file != -1 && mainw->blend_file != -1 && mainw->blend_file != mainw->current_file &&
7954       mainw->num_tr_applied > 0 && mainw->files[mainw->blend_file] &&
7955       mainw->files[mainw->blend_file]->clip_type == CLIP_TYPE_GENERATOR) {
7956     ////////////////////////// switching background generator: stop the old one first
7957     weed_generator_end((weed_plant_t *)mainw->files[mainw->blend_file]->ext_src);
7958     mainw->current_file = mainw->blend_file;
7959   }
7960 
7961   // old_file can also be -1 if we are doing a fg_modeswitch
7962   if (old_file > -1 && LIVES_IS_PLAYING && mainw->num_tr_applied > 0) is_bg = TRUE;
7963 
7964   filter = weed_instance_get_filter(inst, FALSE);
7965   filter_name = weed_get_string_value(filter, WEED_LEAF_NAME, &error);
7966 
7967   if (CURRENT_CLIP_IS_VALID && !CURRENT_CLIP_HAS_VIDEO) {
7968     // audio clip - we will init the generator as fg video in the same clip
7969     // otherwise known as "showoff mode"
7970     cfile->frames = 1;
7971     cfile->start = cfile->end = 1;
7972     cfile->clip_type = CLIP_TYPE_GENERATOR;
7973     cfile->frameno = 1;
7974     mainw->play_start = mainw->play_end = 1;
7975     mainw->startticks = mainw->currticks;
7976   } else {
7977     if (!create_cfile(-1, filter_name, TRUE)) {
7978       filter_mutex_unlock(key);
7979       return 3;
7980     }
7981 
7982     cfile->clip_type = CLIP_TYPE_GENERATOR;
7983 
7984     lives_snprintf(cfile->type, 40, "generator:%s", filter_name);
7985     lives_snprintf(cfile->file_name, PATH_MAX, "generator: %s", filter_name);
7986     lives_snprintf(cfile->name, CLIP_NAME_MAXLEN, "generator: %s", filter_name);
7987     lives_free(filter_name);
7988 
7989     // open as a clip with 1 frame
7990     cfile->start = cfile->end = cfile->frames = 1;
7991   }
7992 
7993   new_file = mainw->current_file;
7994   cfile->ext_src = inst;
7995   cfile->ext_src_type = LIVES_EXT_SRC_FILTER;
7996 
7997   if (is_bg) {
7998     mainw->blend_file = mainw->current_file;
7999     if (mainw->ce_thumbs && mainw->active_sa_clips == SCREEN_AREA_BACKGROUND) ce_thumbs_highlight_current_clip();
8000   }
8001 
8002   if (!is_bg || old_file == -1 || old_file == new_file) fg_generator_clip = new_file;
8003 
8004   if (weed_plant_has_leaf(filter, WEED_LEAF_HOST_FPS))
8005     cfile->pb_fps = cfile->fps = weed_get_double_value(filter, WEED_LEAF_HOST_FPS, &error);
8006   else if (weed_plant_has_leaf(filter, WEED_LEAF_PREFERRED_FPS))
8007     cfile->pb_fps = cfile->fps = weed_get_double_value(filter, WEED_LEAF_PREFERRED_FPS, &error);
8008   else {
8009     cfile->pb_fps = cfile->fps = prefs->default_fps;
8010   }
8011 
8012   out_channels = weed_get_plantptr_array_counted(inst, WEED_LEAF_OUT_CHANNELS, &num_channels);
8013   if (num_channels  == 0) {
8014     filter_mutex_unlock(key);
8015     cfile->ext_src = NULL;
8016     cfile->ext_src_type = LIVES_EXT_SRC_NONE;
8017     close_current_file(mainw->pre_src_file);
8018     return 4;
8019   }
8020 
8021   if (!(channel = get_enabled_channel(inst, 0, FALSE))) {
8022     lives_free(out_channels);
8023     filter_mutex_unlock(key);
8024     cfile->ext_src = NULL;
8025     cfile->ext_src_type = LIVES_EXT_SRC_NONE;
8026     close_current_file(mainw->pre_src_file);
8027     return 5;
8028   }
8029   lives_free(out_channels);
8030 
8031   cfile->hsize = weed_get_int_value(channel, WEED_LEAF_WIDTH, &error);
8032   cfile->vsize = weed_get_int_value(channel, WEED_LEAF_HEIGHT, &error);
8033 
8034   palette = weed_get_int_value(channel, WEED_LEAF_CURRENT_PALETTE, &error);
8035   if (palette == WEED_PALETTE_RGBA32 || palette == WEED_PALETTE_ARGB32 || palette == WEED_PALETTE_BGRA32) cfile->bpp = 32;
8036   else cfile->bpp = 24;
8037 
8038   cfile->opening = FALSE;
8039   mainw->proc_ptr = NULL;
8040 
8041   // if the generator has an optional audio in channel, enable it: TODO - make this configurable
8042   if ((achan = get_audio_channel_in(inst, 0)) != NULL) {
8043     if (weed_plant_has_leaf(achan, WEED_LEAF_DISABLED)) weed_leaf_delete(achan, WEED_LEAF_DISABLED);
8044     register_audio_channels(1);
8045   }
8046 
8047   // if not playing, start playing
8048   if (!LIVES_IS_PLAYING) {
8049     uint64_t new_rte;
8050 
8051     if (!is_bg || old_file == -1 || old_file == new_file) {
8052       switch_to_file((mainw->current_file = old_file), new_file);
8053       set_main_title(cfile->file_name, 0);
8054       mainw->play_start = 1;
8055       mainw->play_end = INT_MAX;
8056       if (is_bg) {
8057         mainw->blend_file = mainw->current_file;
8058         if (old_file != -1) mainw->current_file = old_file;
8059       }
8060     } else {
8061       mainw->blend_file = mainw->current_file;
8062       mainw->current_file = old_file;
8063       mainw->play_start = cfile->start;
8064       mainw->play_end = cfile->end;
8065       mainw->playing_sel = FALSE;
8066     }
8067 
8068     new_rte = GU641 << key;
8069     pthread_mutex_lock(&mainw->event_list_mutex);
8070     if (!(mainw->rte & new_rte)) mainw->rte |= new_rte;
8071     pthread_mutex_unlock(&mainw->event_list_mutex);
8072 
8073     mainw->last_grabbable_effect = key;
8074     if (rte_window) rtew_set_keych(key, TRUE);
8075     if (mainw->ce_thumbs) {
8076       ce_thumbs_set_keych(key, TRUE);
8077 
8078       // if effect was auto (from ACTIVATE data connection), leave all param boxes
8079       // otherwise, remove any which are not "pinned"
8080       if (!mainw->fx_is_auto) ce_thumbs_add_param_box(key, !mainw->fx_is_auto);
8081     }
8082     filter_mutex_unlock(key);
8083 
8084     weed_instance_unref(inst);  // release ref from weed_instance_from_filter, normally we would do this on return
8085 
8086     if (mainw->play_window) {
8087       lives_widget_queue_draw(mainw->play_window);
8088     }
8089     mainw->gen_started_play = TRUE;
8090 
8091     if (!mainw->osc_auto) start_playback_async(6);
8092     else {
8093       on_playall_activate(NULL, NULL);
8094       // need to set this after playback ends; this stops the key from being activated (again) in effects.c
8095       // also stops the (now defunct instance being unreffed)
8096       mainw->gen_started_play = TRUE;
8097     }
8098     return 0;
8099   } else {
8100     // already playing
8101 
8102     if (old_file != -1 && mainw->files[old_file]) {
8103       if (IS_NORMAL_CLIP(old_file)) mainw->pre_src_file = old_file;
8104       mainw->current_file = old_file;
8105     }
8106 
8107     if (!is_bg || old_file == -1 || old_file == new_file) {
8108       //if (mainw->current_file == -1) mainw->current_file = new_file;
8109 
8110       if (new_file != old_file) {
8111         mainw->new_clip = new_file;
8112 
8113         /* filter_mutex_unlock(key); // else we get stuck pulling a frame */
8114         /* do_quick_switch(new_file); */
8115         /* filter_mutex_lock(key); */
8116         /* mainw->force_show = TRUE; */
8117 
8118         // switch audio clip
8119         /* if (is_realtime_aplayer(prefs->audio_player) && (prefs->audio_opts & AUDIO_OPTS_FOLLOW_CLIPS) */
8120         /*     && !mainw->is_rendering && (mainw->preview || !(mainw->agen_key != 0 || prefs->audio_src == AUDIO_SRC_EXT))) { */
8121         /*   switch_audio_clip(new_file, TRUE); */
8122         /* } */
8123 
8124         if (mainw->files[mainw->new_blend_file]) mainw->blend_file = mainw->new_blend_file;
8125         if (!is_bg && IS_VALID_CLIP(blend_file)) mainw->blend_file = blend_file;
8126         mainw->new_blend_file = -1;
8127       } else {
8128         lives_widget_show_all(mainw->playframe);
8129         resize(1);
8130         lives_widget_set_opacity(mainw->playframe, 1.);
8131       }
8132       //if (old_file==-1) mainw->whentostop=STOP_ON_VID_END;
8133     } else {
8134       if (mainw->current_file == -1) {
8135         /* mainw->current_file = new_file; */
8136         /* if (is_realtime_aplayer(prefs->audio_player) && (prefs->audio_opts & AUDIO_OPTS_FOLLOW_CLIPS) */
8137         /*     && !mainw->is_rendering && (mainw->preview || !(mainw->agen_key != 0 || prefs->audio_src == AUDIO_SRC_EXT))) { */
8138         /*   switch_audio_clip(new_file, TRUE); */
8139         /* } */
8140       } else mainw->blend_file = new_file;
8141       if (mainw->ce_thumbs && (mainw->active_sa_clips == SCREEN_AREA_BACKGROUND
8142                                || mainw->active_sa_clips == SCREEN_AREA_FOREGROUND))
8143         ce_thumbs_highlight_current_clip();
8144     }
8145     if (mainw->cancelled == CANCEL_GENERATOR_END) mainw->cancelled = CANCEL_NONE;
8146   }
8147 
8148   filter_mutex_unlock(key);
8149 
8150   return 0;
8151 }
8152 
8153 
wge_inner(weed_plant_t * inst)8154 void wge_inner(weed_plant_t *inst) {
8155   weed_plant_t *next_inst = NULL;
8156   // called from weed_generator_end() below and also called directly after playback ends
8157 
8158   // during playback, MUST call this with filter_mutex locked
8159 
8160   // unrefs inst 1 times
8161 
8162   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) {
8163     int error;
8164     int key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, &error);
8165     key_to_instance[key][key_modes[key]] = NULL;
8166   }
8167 
8168   while (inst) {
8169     next_inst = get_next_compound_inst(inst);
8170     weed_call_deinit_func(inst);
8171     inst = next_inst;
8172   }
8173 }
8174 
8175 
weed_generator_end(weed_plant_t * inst)8176 void weed_generator_end(weed_plant_t *inst) {
8177   // generator has stopped for one of the following reasons:
8178   // efect was de-inited; clip (bg/fg) was changed; playback stopped with fg
8179 
8180   // during playback, MUST be called with filter_mutex locked
8181 
8182   lives_whentostop_t wts = mainw->whentostop;
8183   boolean is_bg = FALSE;
8184   boolean clip_switched = mainw->clip_switched;
8185   int current_file = mainw->current_file, pre_src_file = mainw->pre_src_file;
8186 
8187   if (LIVES_IS_PLAYING) mainw->ignore_clipswitch = TRUE;
8188 
8189   if (!inst) {
8190     LIVES_WARN("inst was NULL !");
8191   }
8192 
8193   if (mainw->blend_file != -1 && mainw->blend_file != current_file && mainw->files[mainw->blend_file] &&
8194       mainw->files[mainw->blend_file]->ext_src == inst) is_bg = TRUE;
8195   else mainw->new_blend_file = mainw->blend_file;
8196 
8197   if (LIVES_IS_PLAYING && !is_bg && mainw->whentostop == STOP_ON_VID_END) {
8198     // we will close the file after playback stops
8199     // and also unref the instance
8200     mainw->cancelled = CANCEL_GENERATOR_END;
8201     return;
8202   }
8203 
8204   /////// update the key checkboxes //////
8205   /// TODO: do we want to do this if switching from one gen to another ?
8206   if (rte_window  && !mainw->is_rendering && !mainw->multitrack) {
8207     // update real time effects window if we are showing it
8208     if (!is_bg) {
8209       rtew_set_keych(fg_generator_key, FALSE);
8210     } else {
8211       rtew_set_keych(bg_generator_key, FALSE);
8212     }
8213   }
8214 
8215   if (mainw->ce_thumbs) {
8216     // update ce_thumbs window if we are showing it
8217     if (!is_bg) {
8218       ce_thumbs_set_keych(fg_generator_key, FALSE);
8219     } else {
8220       ce_thumbs_set_keych(bg_generator_key, FALSE);
8221     }
8222   }
8223 
8224   if (inst && get_audio_channel_in(inst, 0)) {
8225     unregister_audio_channels(1);
8226   }
8227 
8228   if (is_bg) {
8229     if (mainw->blend_layer) check_layer_ready(mainw->blend_layer);
8230     key_to_instance[bg_generator_key][bg_generator_mode] = NULL;
8231     pthread_mutex_lock(&mainw->event_list_mutex);
8232     if (rte_key_is_enabled(1 + bg_generator_key)) mainw->rte ^= (GU641 << bg_generator_key);
8233     pthread_mutex_unlock(&mainw->event_list_mutex);
8234     bg_gen_to_start = bg_generator_key = bg_generator_mode = -1;
8235     pre_src_file = mainw->pre_src_file;
8236     mainw->pre_src_file = mainw->current_file;
8237   } else {
8238     if (mainw->frame_layer) check_layer_ready(mainw->frame_layer);
8239     key_to_instance[fg_generator_key][fg_generator_mode] = NULL;
8240     pthread_mutex_lock(&mainw->event_list_mutex);
8241     if (rte_key_is_enabled(1 + fg_generator_key)) mainw->rte ^= (GU641 << fg_generator_key);
8242     pthread_mutex_unlock(&mainw->event_list_mutex);
8243     fg_gen_to_start = fg_generator_key = fg_generator_clip = fg_generator_mode = -1;
8244     if (mainw->blend_file == mainw->current_file) mainw->blend_file = -1;
8245   }
8246 
8247   if (inst) wge_inner(inst); // removes 1 ref
8248 
8249   // if the param window is already open, show any reinits now
8250   if (fx_dialog[1]) {
8251     if (is_bg) update_widget_vis(NULL, bg_generator_key, bg_generator_mode); // redraw our paramwindow
8252     else update_widget_vis(NULL, fg_generator_key, fg_generator_mode); // redraw our paramwindow
8253   }
8254 
8255   if (!is_bg && cfile->achans > 0 && cfile->clip_type == CLIP_TYPE_GENERATOR) {
8256     // we started playing from an audio clip
8257     cfile->frames = cfile->start = cfile->end = 0;
8258     cfile->ext_src = NULL;
8259     cfile->ext_src_type = LIVES_EXT_SRC_NONE;
8260     cfile->clip_type = CLIP_TYPE_DISK;
8261     cfile->hsize = cfile->vsize = 0;
8262     cfile->pb_fps = cfile->fps = prefs->default_fps;
8263     return;
8264   }
8265 
8266   /// here we must be very careful, because we are about to close the clip which is either playing as fg or bg
8267   /// in the case of background, we just switch to it very briefly to close it, then back to the fg clip
8268   /// in case the generator was ended by a change of bg clip, we restore the new bg clip
8269   ///
8270   /// in the case of the fg clip we close it and try to switch to another valid clip
8271   /// we switch back to the old (invalid) clip after this, while setting mainw->new_clip to the new one
8272   /// the player will detect the invalid clip and jump to the switch point to handle the changeover cleanly
8273   if (is_bg) {
8274     mainw->pre_src_file = mainw->current_file;
8275     mainw->current_file = mainw->blend_file;
8276     mainw->blend_file = mainw->new_blend_file;
8277     mainw->new_blend_file = -1;
8278     // close generator file and switch to original file if possible
8279     if (!cfile || cfile->clip_type != CLIP_TYPE_GENERATOR) {
8280       break_me("close non-gen file");
8281       LIVES_WARN("Close non-generator file 2");
8282       mainw->current_file = mainw->pre_src_file;
8283     } else {
8284       cfile->ext_src = NULL;
8285       cfile->ext_src_type = LIVES_EXT_SRC_NONE;
8286       close_current_file(mainw->pre_src_file);
8287     }
8288     if (mainw->ce_thumbs && mainw->active_sa_clips == SCREEN_AREA_BACKGROUND) ce_thumbs_update_current_clip();
8289   } else {
8290     // close generator file and switch to original file if possible
8291     if (!cfile || cfile->clip_type != CLIP_TYPE_GENERATOR) {
8292       LIVES_WARN("Close non-generator file 1");
8293     } else {
8294       cfile->ext_src = NULL;
8295       cfile->ext_src_type = LIVES_EXT_SRC_NONE;
8296       if (cfile->achans == 0) {
8297         current_file = mainw->current_file;
8298         close_current_file(mainw->pre_src_file);
8299         if (LIVES_IS_PLAYING) {
8300           mainw->new_clip = mainw->pre_src_file;
8301           mainw->current_file = current_file;
8302         }
8303       }
8304     }
8305     if (mainw->current_file == current_file) mainw->clip_switched = clip_switched;
8306   }
8307 
8308   if (is_bg) {
8309     mainw->current_file = current_file;
8310     mainw->pre_src_file = pre_src_file;
8311     mainw->whentostop = wts;
8312   }
8313 
8314   if (mainw->current_file == -1) mainw->cancelled = CANCEL_GENERATOR_END;
8315 }
8316 
8317 
weed_bg_generator_end(weed_plant_t * inst)8318 void weed_bg_generator_end(weed_plant_t *inst) {
8319   // when we stop with a bg generator we want it to be restarted next time
8320   // i.e we will need a new clip for it
8321   int bg_gen_key = bg_generator_key;
8322 
8323   // filter_mutex unlocked
8324 
8325   /// ref the isntance so it isn't deleted
8326   weed_instance_ref(inst);
8327   weed_generator_end(inst); // unrefs inst
8328   bg_gen_to_start = bg_gen_key;
8329 }
8330 
8331 
weed_playback_gen_start(void)8332 boolean weed_playback_gen_start(void) {
8333   // init generators on pb. We have to do this after audio startup
8334   // filter_mutex unlocked
8335   weed_plant_t *inst = NULL, *filter;
8336   weed_plant_t *next_inst = NULL, *orig_inst;
8337 
8338   char *filter_name;
8339 
8340   weed_error_t error = WEED_SUCCESS;
8341 
8342   int bgs = bg_gen_to_start;
8343   boolean was_started = FALSE;
8344 
8345   if (mainw->is_rendering) return TRUE;
8346 
8347   if (fg_gen_to_start == bg_gen_to_start) bg_gen_to_start = -1;
8348 
8349   if (cfile->frames == 0 && fg_gen_to_start == -1 && bg_gen_to_start != -1) {
8350     fg_gen_to_start = bg_gen_to_start;
8351     bg_gen_to_start = -1;
8352   }
8353 
8354   mainw->osc_block = TRUE;
8355 
8356   if (fg_gen_to_start != -1) {
8357     filter_mutex_lock(fg_gen_to_start);
8358     // check is still gen
8359 
8360     if (enabled_in_channels(weed_filters[key_to_fx[fg_gen_to_start][key_modes[fg_gen_to_start]]], FALSE) == 0) {
8361       orig_inst = inst = weed_instance_obtain(fg_gen_to_start, key_modes[fg_gen_to_start]);
8362 
8363       if (inst) {
8364 geninit1:
8365 
8366         filter = weed_instance_get_filter(inst, FALSE);
8367         if (weed_plant_has_leaf(filter, WEED_LEAF_INIT_FUNC)) {
8368           weed_init_f init_func = (weed_init_f)weed_get_funcptr_value(filter, WEED_LEAF_INIT_FUNC, NULL);
8369           if (init_func) {
8370             char *cwd = cd_to_plugin_dir(filter);
8371             error = (*init_func)(inst);
8372             lives_chdir(cwd, FALSE);
8373             lives_free(cwd);
8374           }
8375         }
8376 
8377         if (error != WEED_SUCCESS) {
8378           weed_plant_t *oldinst = inst;
8379           orig_inst = inst = weed_instance_obtain(fg_gen_to_start, key_modes[fg_gen_to_start]);
8380           weed_instance_unref(oldinst);
8381           key_to_instance[fg_gen_to_start][key_modes[fg_gen_to_start]] = NULL;
8382           if (inst) {
8383             char *tmp;
8384             filter = weed_instance_get_filter(inst, TRUE);
8385             filter_name = weed_get_string_value(filter, WEED_LEAF_NAME, NULL);
8386             d_print(_("Failed to start generator %s (%s)\n"), filter_name,
8387                     (tmp = lives_strdup(weed_error_to_text(error))));
8388             lives_free(tmp);
8389             lives_free(filter_name);
8390 
8391 deinit4:
8392             next_inst = get_next_compound_inst(inst);
8393 
8394             weed_call_deinit_func(inst);
8395 
8396             if (next_inst) {
8397               // handle compound fx
8398               inst = next_inst;
8399               if (weed_get_boolean_value(inst, WEED_LEAF_HOST_INITED, &error) == WEED_TRUE) goto deinit4;
8400             }
8401           }
8402 
8403           // unref twice to destroy
8404           weed_instance_unref(orig_inst);
8405           weed_instance_unref(orig_inst);
8406 
8407           filter_mutex_unlock(fg_gen_to_start);
8408           fg_gen_to_start = -1;
8409           cfile->ext_src = NULL;
8410           cfile->ext_src_type = LIVES_EXT_SRC_NONE;
8411           mainw->osc_block = FALSE;
8412           return FALSE;
8413         }
8414 
8415         weed_set_boolean_value(inst, WEED_LEAF_HOST_INITED, WEED_TRUE);
8416         weed_set_boolean_value(inst, WEED_LEAF_HOST_UNUSED, WEED_TRUE);
8417 
8418         if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE)) {
8419           inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, &error);
8420           goto geninit1;
8421         }
8422 
8423         inst = orig_inst;
8424 
8425         if (weed_plant_has_leaf(filter, WEED_LEAF_PREFERRED_FPS)) {
8426           int current_file = mainw->current_file;
8427           mainw->current_file = fg_generator_clip;
8428           cfile->fps = weed_get_double_value(filter, WEED_LEAF_PREFERRED_FPS, &error);
8429           set_main_title(cfile->file_name, 0);
8430           lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), cfile->fps);
8431           mainw->current_file = current_file;
8432         }
8433 
8434         mainw->clip_switched = TRUE;
8435         cfile->ext_src = inst;
8436         cfile->ext_src_type = LIVES_EXT_SRC_FILTER;
8437         weed_instance_unref(inst);
8438       }
8439     }
8440 
8441     filter_mutex_unlock(fg_gen_to_start);
8442     fg_gen_to_start = -1;
8443   }
8444 
8445   inst = NULL;
8446 
8447   if (bg_gen_to_start != -1) {
8448     filter_mutex_lock(bg_gen_to_start);
8449 
8450     // check is still gen
8451     if (mainw->num_tr_applied > 0
8452         && enabled_in_channels(weed_filters[key_to_fx[bg_gen_to_start][key_modes[bg_gen_to_start]]], FALSE) == 0) {
8453       if ((inst = weed_instance_obtain(bg_gen_to_start, key_modes[bg_gen_to_start])) == NULL) {
8454         // restart bg generator
8455         if (!weed_init_effect(bg_gen_to_start)) {
8456           mainw->osc_block = FALSE;
8457           filter_mutex_unlock(bg_gen_to_start);
8458           return TRUE;
8459         }
8460         was_started = TRUE;
8461       }
8462 
8463       if (!inst) inst = weed_instance_obtain(bgs, key_modes[bgs]);
8464 
8465       orig_inst = inst;
8466 
8467       if (!inst) {
8468         // 2nd playback
8469         int playing_file = mainw->playing_file;
8470         mainw->playing_file = -100; //kludge to stop playing a second time
8471         if (!weed_init_effect(bg_gen_to_start)) {
8472           filter_mutex_unlock(bg_gen_to_start);
8473           error++;
8474         }
8475         mainw->playing_file = playing_file;
8476         orig_inst = weed_instance_obtain(bg_gen_to_start, key_modes[bg_gen_to_start]);
8477       } else {
8478         if (!was_started) {
8479           orig_inst = inst;
8480 genstart2:
8481 
8482           // TODO - error check
8483           weed_call_init_func(inst);
8484 
8485           // handle compound fx
8486           if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE)) {
8487             inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, &error);
8488             goto genstart2;
8489           }
8490         }
8491       }
8492 
8493       inst = orig_inst;
8494 
8495       if (error != WEED_SUCCESS) {
8496 undoit:
8497         key_to_instance[bg_gen_to_start][key_modes[bg_gen_to_start]] = NULL;
8498         if (inst) {
8499           char *tmp;
8500           filter = weed_instance_get_filter(inst, TRUE);
8501           filter_name = weed_get_string_value(filter, WEED_LEAF_NAME, NULL);
8502           d_print(_("Failed to start generator %s, (%s)\n"), filter_name, (tmp = lives_strdup(weed_error_to_text(error))));
8503           lives_free(tmp);
8504           lives_free(filter_name);
8505 
8506 deinit5:
8507 
8508           if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE))
8509             next_inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE,
8510                                                 &error);
8511           else next_inst = NULL;
8512 
8513           weed_call_deinit_func(inst);
8514           weed_instance_unref(inst);
8515           weed_instance_unref(inst);
8516 
8517           if (next_inst) {
8518             // handle compound fx
8519             inst = next_inst;
8520             weed_instance_ref(inst);
8521             goto deinit5;
8522           }
8523         }
8524 
8525         mainw->blend_file = -1;
8526         pthread_mutex_lock(&mainw->event_list_mutex);
8527         if (rte_key_is_enabled(1 + ABS(bg_gen_to_start))) mainw->rte ^= (GU641 << ABS(bg_gen_to_start));
8528         pthread_mutex_unlock(&mainw->event_list_mutex);
8529         mainw->osc_block = FALSE;
8530         filter_mutex_unlock(bg_gen_to_start);
8531         bg_gen_to_start = -1;
8532         return FALSE;
8533       }
8534 
8535       if (!IS_VALID_CLIP(mainw->blend_file)
8536           || (mainw->files[mainw->blend_file]->frames > 0
8537               && mainw->files[mainw->blend_file]->clip_type != CLIP_TYPE_GENERATOR)) {
8538         int current_file = mainw->current_file;
8539 
8540         filter = weed_instance_get_filter(inst, TRUE);
8541         filter_name = weed_get_string_value(filter, WEED_LEAF_NAME, NULL);
8542         if (!create_cfile(-1, filter_name, TRUE)) {
8543           mainw->current_file = current_file;
8544           goto undoit;
8545         }
8546 
8547         cfile->clip_type = CLIP_TYPE_GENERATOR;
8548 
8549         lives_snprintf(cfile->type, 40, "generator:%s", filter_name);
8550         lives_snprintf(cfile->file_name, PATH_MAX, "generator: %s", filter_name);
8551         lives_snprintf(cfile->name, CLIP_NAME_MAXLEN, "generator: %s", filter_name);
8552         lives_free(filter_name);
8553 
8554         // open as a clip with 1 frame
8555         cfile->start = cfile->end = cfile->frames = 1;
8556         mainw->blend_file = mainw->current_file;
8557         mainw->files[mainw->blend_file]->ext_src = inst;
8558         mainw->files[mainw->blend_file]->ext_src_type = LIVES_EXT_SRC_FILTER;
8559         mainw->current_file = current_file;
8560       }
8561     }
8562     filter_mutex_unlock(bg_gen_to_start);
8563   }
8564 
8565   orig_inst = inst;
8566 
8567 setgui1:
8568 
8569   // handle compound fx
8570   if (inst && weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE)) {
8571     inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, &error);
8572     goto setgui1;
8573   }
8574 
8575   if (orig_inst) weed_instance_unref(orig_inst);
8576   bg_gen_to_start = -1;
8577   mainw->osc_block = FALSE;
8578 
8579   return TRUE;
8580 }
8581 
8582 //////////////////////////////////////////////////////////////////////////////
8583 // weed parameter functions
8584 
8585 /// returns the permanent (structural) state
8586 /// c.f check_hidden_gui() which sets flag values for params linked to an rfx extension
is_hidden_param(weed_plant_t * plant,int i)8587 boolean is_hidden_param(weed_plant_t *plant, int i) {
8588   // find out if in_param i is visible or not for plant. Plant can be an instance or a filter
8589   weed_plant_t **wtmpls;
8590   weed_plant_t *filter, *pgui = NULL;
8591   weed_plant_t *wtmpl;
8592   boolean visible = TRUE;
8593   int num_params = 0;
8594 
8595   if (WEED_PLANT_IS_FILTER_INSTANCE(plant)) {
8596     weed_plant_t *param = weed_inst_in_param(plant, i, FALSE, FALSE);
8597     if (param) {
8598       if (weed_param_is_hidden(param, WEED_FALSE) == WEED_TRUE) {
8599         return TRUE;
8600       }
8601       pgui = weed_param_get_gui(param, FALSE);
8602     }
8603     filter = weed_instance_get_filter(plant, TRUE);
8604   } else filter = plant;
8605 
8606   wtmpls = weed_filter_get_in_paramtmpls(filter, &num_params);
8607 
8608   if (i >= num_params) {
8609     lives_free(wtmpls);
8610     return TRUE;
8611   }
8612 
8613   wtmpl = wtmpls[i];
8614   if (!wtmpl) {
8615     lives_free(wtmpls);
8616     return TRUE;
8617   }
8618 
8619   // hide internally connected parameters for compound fx
8620   if (weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_INTERNAL_CONNECTION)) {
8621     lives_free(wtmpls);
8622     return TRUE;
8623   }
8624 
8625   /// if we are to copy the values to another param, make sure it's possible (type, num values)
8626   if ((weed_plant_has_leaf(wtmpl, WEED_LEAF_COPY_VALUE_TO))
8627       || (pgui && weed_plant_has_leaf(pgui, WEED_LEAF_COPY_VALUE_TO))) {
8628     int copyto = -1;
8629     int flags2 = 0, param_type, param_type2;
8630     weed_plant_t *wtmpl2;
8631     if (weed_plant_has_leaf(wtmpl, WEED_LEAF_COPY_VALUE_TO))
8632       copyto = weed_get_int_value(wtmpl, WEED_LEAF_COPY_VALUE_TO, NULL);
8633     if (pgui && weed_plant_has_leaf(pgui, WEED_LEAF_COPY_VALUE_TO))
8634       copyto = weed_get_int_value(pgui, WEED_LEAF_COPY_VALUE_TO, NULL);
8635     if (copyto == i || copyto < 0) copyto = -1;
8636     if (copyto > -1) {
8637       visible = FALSE;
8638       wtmpl2 = wtmpls[copyto];
8639       flags2 = weed_paramtmpl_get_flags(wtmpl2);
8640       param_type = weed_paramtmpl_get_type(wtmpl);
8641       param_type2 = weed_paramtmpl_get_type(wtmpl2);
8642       if (param_type == param_type2
8643           && ((flags2 & WEED_PARAMETER_VARIABLE_SIZE)
8644               || (flags2 & WEED_PARAMETER_VALUE_PER_CHANNEL)
8645               || weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT)
8646               == weed_leaf_num_elements(wtmpl2, WEED_LEAF_DEFAULT))) {
8647         visible = TRUE;
8648       }
8649     }
8650   }
8651   lives_free(wtmpls);
8652   return !visible;
8653 }
8654 
8655 
get_transition_param(weed_plant_t * filter,boolean skip_internal)8656 int get_transition_param(weed_plant_t *filter, boolean skip_internal) {
8657   int num_params, count = 0;
8658   weed_plant_t **in_ptmpls = weed_filter_get_in_paramtmpls(filter, &num_params);
8659   if (num_params == 0) return -1;
8660   for (int i = 0; i < num_params; i++) {
8661     if (skip_internal && weed_plant_has_leaf(in_ptmpls[i], WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
8662     if (weed_get_boolean_value(in_ptmpls[i], WEED_LEAF_IS_TRANSITION, NULL) == WEED_TRUE) {
8663       lives_free(in_ptmpls);
8664       return count;
8665     }
8666     count++;
8667   }
8668   lives_free(in_ptmpls);
8669   return -1;
8670 }
8671 
8672 
get_master_vol_param(weed_plant_t * filter,boolean skip_internal)8673 int get_master_vol_param(weed_plant_t *filter, boolean skip_internal) {
8674   int error, num_params, i, count = 0;
8675   weed_plant_t **in_ptmpls;
8676 
8677   in_ptmpls = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, &num_params);
8678   if (num_params == 0) return -1;
8679   for (i = 0; i < num_params; i++) {
8680     if (skip_internal && weed_plant_has_leaf(in_ptmpls[i], WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
8681     if (weed_plant_has_leaf(in_ptmpls[i], WEED_LEAF_IS_VOLUME_MASTER) &&
8682         weed_get_boolean_value(in_ptmpls[i], WEED_LEAF_IS_VOLUME_MASTER, &error) == WEED_TRUE) {
8683       lives_free(in_ptmpls);
8684       return count;
8685     }
8686     count++;
8687   }
8688   lives_free(in_ptmpls);
8689   return -1;
8690 }
8691 
8692 
is_perchannel_multiw(weed_plant_t * param)8693 boolean is_perchannel_multiw(weed_plant_t *param) {
8694   // updated for weed spec 1.1
8695   int error;
8696   int flags = 0;
8697   weed_plant_t *ptmpl;
8698   if (WEED_PLANT_IS_PARAMETER(param)) ptmpl = weed_get_plantptr_value(param, WEED_LEAF_TEMPLATE, &error);
8699   else ptmpl = param;
8700   if (weed_plant_has_leaf(ptmpl, WEED_LEAF_FLAGS)) flags = weed_get_int_value(ptmpl, WEED_LEAF_FLAGS, &error);
8701   if (flags & WEED_PARAMETER_VALUE_PER_CHANNEL) return TRUE;
8702   return FALSE;
8703 }
8704 
8705 
has_perchannel_multiw(weed_plant_t * filter)8706 boolean has_perchannel_multiw(weed_plant_t *filter) {
8707   int nptmpl, i;
8708   weed_plant_t **ptmpls = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, &nptmpl);
8709 
8710   if (nptmpl == 0) return FALSE;
8711 
8712   for (i = 0; i < nptmpl; i++) {
8713     if (is_perchannel_multiw(ptmpls[i])) {
8714       lives_free(ptmpls);
8715       return TRUE;
8716     }
8717   }
8718 
8719   lives_free(ptmpls);
8720   return FALSE;
8721 }
8722 
8723 
weed_inst_in_param(weed_plant_t * inst,int param_num,boolean skip_hidden,boolean skip_internal)8724 weed_plant_t *weed_inst_in_param(weed_plant_t *inst, int param_num, boolean skip_hidden, boolean skip_internal) {
8725   weed_plant_t **in_params;
8726   weed_plant_t *param;
8727   int error, num_params;
8728 
8729   do {
8730     if (!weed_plant_has_leaf(inst, WEED_LEAF_IN_PARAMETERS)) continue; // has no in_parameters
8731 
8732     num_params = weed_leaf_num_elements(inst, WEED_LEAF_IN_PARAMETERS);
8733 
8734     if (!skip_hidden && !skip_internal) {
8735       if (num_params > param_num) {
8736         in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, &error);
8737         param = in_params[param_num];
8738         lives_free(in_params);
8739         return param;
8740       }
8741       param_num -= num_params;
8742     }
8743 
8744     else {
8745       int count = 0;
8746       register int i;
8747 
8748       in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, &error);
8749 
8750       for (i = 0; i < num_params; i++) {
8751         param = in_params[i];
8752         if ((!skip_hidden || !is_hidden_param(inst, i)) && (!skip_internal
8753             || !weed_plant_has_leaf(param, WEED_LEAF_HOST_INTERNAL_CONNECTION))) {
8754           if (count == param_num) {
8755             lives_free(in_params);
8756             return param;
8757           }
8758           count++;
8759         }
8760       }
8761       param_num -= count;
8762       lives_free(in_params);
8763     }
8764   } while (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE) &&
8765            (inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, &error)) != NULL);
8766 
8767   return NULL;
8768 }
8769 
8770 
weed_inst_out_param(weed_plant_t * inst,int param_num)8771 weed_plant_t *weed_inst_out_param(weed_plant_t *inst, int param_num) {
8772   weed_plant_t **out_params;
8773   weed_plant_t *param;
8774   int error, num_params;
8775 
8776   do {
8777     if (!weed_plant_has_leaf(inst, WEED_LEAF_OUT_PARAMETERS)) continue; // has no out_parameters
8778 
8779     num_params = weed_leaf_num_elements(inst, WEED_LEAF_OUT_PARAMETERS);
8780 
8781     if (num_params > param_num) {
8782       out_params = weed_get_plantptr_array(inst, WEED_LEAF_OUT_PARAMETERS, &error);
8783       param = out_params[param_num];
8784       lives_free(out_params);
8785       return param;
8786     }
8787     param_num -= num_params;
8788   } while (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE) &&
8789            (inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, &error)) != NULL);
8790 
8791   return NULL;
8792 }
8793 
8794 
weed_filter_in_paramtmpl(weed_plant_t * filter,int param_num,boolean skip_internal)8795 weed_plant_t *weed_filter_in_paramtmpl(weed_plant_t *filter, int param_num, boolean skip_internal) {
8796   weed_plant_t **in_params;
8797   weed_plant_t *ptmpl;
8798   int num_params;
8799 
8800   int count = 0;
8801   register int i;
8802 
8803   in_params = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, &num_params);
8804   if (num_params <= param_num) {
8805     lives_freep((void **)&in_params);
8806     return NULL; // invalid parameter number
8807   }
8808 
8809   if (!skip_internal) {
8810     ptmpl = in_params[param_num];
8811     lives_free(in_params);
8812     return ptmpl;
8813   }
8814 
8815   for (i = 0; i < num_params; i++) {
8816     ptmpl = in_params[i];
8817     if (!weed_plant_has_leaf(ptmpl, WEED_LEAF_HOST_INTERNAL_CONNECTION)) {
8818       if (count == param_num) {
8819         lives_free(in_params);
8820         return ptmpl;
8821       }
8822       count++;
8823     }
8824   }
8825 
8826   lives_free(in_params);
8827   return NULL;
8828 }
8829 
8830 
weed_filter_out_paramtmpl(weed_plant_t * filter,int param_num)8831 weed_plant_t *weed_filter_out_paramtmpl(weed_plant_t *filter, int param_num) {
8832   weed_plant_t **out_params;
8833   weed_plant_t *ptmpl;
8834   int error, num_params;
8835 
8836   if (!weed_plant_has_leaf(filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES)) return NULL; // has no out_parameters
8837 
8838   num_params = weed_leaf_num_elements(filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES);
8839 
8840   if (num_params <= param_num) return NULL; // invalid parameter number
8841 
8842   out_params = weed_get_plantptr_array(filter, WEED_LEAF_OUT_PARAMETER_TEMPLATES, &error);
8843 
8844   ptmpl = out_params[param_num];
8845   lives_free(out_params);
8846   return ptmpl;
8847 }
8848 
8849 
get_nth_simple_param(weed_plant_t * plant,int pnum)8850 int get_nth_simple_param(weed_plant_t *plant, int pnum) {
8851   // return the number of the nth "simple" parameter
8852   // we define "simple" as - must be single valued int or float, must not be hidden
8853 
8854   // -1 is returned if no such parameter is found
8855 
8856   weed_plant_t **in_ptmpls;
8857   weed_plant_t *tparamtmpl;
8858   weed_plant_t *gui;
8859   int i, ptype, flags, nparams;
8860 
8861   if (WEED_PLANT_IS_FILTER_INSTANCE(plant)) plant = weed_instance_get_filter(plant, TRUE);
8862 
8863   if (!weed_plant_has_leaf(plant, WEED_LEAF_IN_PARAMETER_TEMPLATES)) return -1;
8864 
8865   in_ptmpls = weed_get_plantptr_array_counted(plant, WEED_LEAF_IN_PARAMETER_TEMPLATES, &nparams);
8866 
8867   for (i = 0; i < nparams; i++) {
8868     tparamtmpl = in_ptmpls[i];
8869     gui = weed_paramtmpl_get_gui(tparamtmpl, FALSE);
8870 
8871     ptype = weed_paramtmpl_get_type(tparamtmpl);
8872 
8873     if (gui && ptype == WEED_PARAM_INTEGER && weed_plant_has_leaf(gui, WEED_LEAF_CHOICES)) continue;
8874 
8875     flags = weed_paramtmpl_get_flags(tparamtmpl);
8876 
8877     if ((ptype == WEED_PARAM_INTEGER || ptype == WEED_PARAM_FLOAT)
8878         && flags == 0 && weed_leaf_num_elements(tparamtmpl, WEED_LEAF_DEFAULT) == 1 &&
8879         !is_hidden_param(plant, i)) {
8880       if (pnum == 0) {
8881         lives_free(in_ptmpls);
8882         return i;
8883       }
8884       pnum--;
8885     }
8886   }
8887   lives_free(in_ptmpls);
8888   return -1;
8889 }
8890 
8891 
count_simple_params(weed_plant_t * plant)8892 int count_simple_params(weed_plant_t *plant) {
8893   int i, ptype, flags, nparams, count = 0;
8894   weed_plant_t **in_ptmpls;
8895   weed_plant_t *tparamtmpl;
8896 
8897   if (WEED_PLANT_IS_FILTER_INSTANCE(plant)) plant = weed_instance_get_filter(plant, TRUE);
8898 
8899   if (!weed_plant_has_leaf(plant, WEED_LEAF_IN_PARAMETER_TEMPLATES)) return count;
8900 
8901   in_ptmpls = weed_get_plantptr_array_counted(plant, WEED_LEAF_IN_PARAMETER_TEMPLATES, &nparams);
8902 
8903   for (i = 0; i < nparams; i++) {
8904     tparamtmpl = in_ptmpls[i];
8905     ptype = weed_paramtmpl_get_type(tparamtmpl);
8906     flags = weed_paramtmpl_get_flags(tparamtmpl);
8907     if ((ptype == WEED_PARAM_INTEGER || ptype == WEED_PARAM_FLOAT)
8908         && flags == 0 && weed_leaf_num_elements(tparamtmpl, WEED_LEAF_DEFAULT) == 1 &&
8909         !is_hidden_param(plant, i)) {
8910       count++;
8911     }
8912   }
8913   lives_free(in_ptmpls);
8914   return count;
8915 }
8916 
8917 
set_copy_to(weed_plant_t * inst,int pnum,lives_rfx_t * rfx,boolean update)8918 int set_copy_to(weed_plant_t *inst, int pnum, lives_rfx_t *rfx, boolean update) {
8919   // if we update a plugin in_parameter, evaluate any "copy_value_to"
8920   // filter_mutex MUST be unlocked
8921   weed_plant_t *paramtmpl;
8922   weed_plant_t *pgui, *in_param2;
8923   weed_plant_t *in_param = weed_inst_in_param(inst, pnum, FALSE, FALSE); // use this here in case of compound fx
8924   int copyto = -1;
8925   int key = -1;
8926   int param_type, param_type2;
8927 
8928   if (!in_param) return -1;
8929 
8930   pgui = weed_param_get_gui(in_param, FALSE);
8931   paramtmpl = weed_param_get_template(in_param);
8932 
8933   if (pgui && weed_plant_has_leaf(pgui, WEED_LEAF_COPY_VALUE_TO)) {
8934     copyto = weed_get_int_value(pgui, WEED_LEAF_COPY_VALUE_TO, NULL);
8935     if (copyto == pnum || copyto < 0) return -1;
8936   }
8937 
8938   if (copyto == -1 && weed_plant_has_leaf(paramtmpl, WEED_LEAF_COPY_VALUE_TO))
8939     copyto = weed_get_int_value(paramtmpl, WEED_LEAF_COPY_VALUE_TO, NULL);
8940   if (copyto == pnum || copyto < 0) return -1;
8941 
8942   if (copyto >= rfx->num_params) return -1;
8943 
8944   if (rfx->params[copyto].change_blocked) return -1; ///< prevent loops
8945 
8946   param_type = weed_param_get_type(in_param);
8947   in_param2 = weed_inst_in_param(inst, copyto, FALSE, FALSE); // use this here in case of compound fx
8948   if (!in_param2) return -1;
8949   if (weed_plant_has_leaf(in_param2, WEED_LEAF_HOST_INTERNAL_CONNECTION)) return -1;
8950   param_type2 = weed_param_get_type(in_param2);
8951 
8952   /// check for compatibility nvalues
8953   if (!(param_type == param_type2 && (!weed_param_has_variable_size(in_param) || weed_param_has_variable_size(in_param2))
8954         && (!weed_param_has_value_perchannel(in_param) || weed_param_has_variable_size(in_param2)
8955             || weed_param_has_value_perchannel(in_param2)))) return -1;
8956 
8957   if (update) {
8958     weed_plant_t *paramtmpl2 = weed_param_get_template(in_param2);
8959     int flags = weed_paramtmpl_get_flags(paramtmpl2);
8960 
8961     if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_KEY)) {
8962       key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL);
8963       filter_mutex_lock(key);
8964     }
8965     if (flags & WEED_PARAMETER_VALUE_PER_CHANNEL) {
8966       int *ign_array;
8967       int nvals = weed_leaf_num_elements(in_param2, WEED_LEAF_VALUE);
8968       int vsize = 1;
8969       if (param_type2 == WEED_PARAM_COLOR) {
8970         int cspace = weed_get_int_value(paramtmpl2, WEED_LEAF_COLORSPACE, NULL);
8971         if (cspace == WEED_COLORSPACE_RGB) vsize = 3;
8972         else vsize = 4;
8973       }
8974       weed_leaf_copy(paramtmpl2, "host_new_def_backup", paramtmpl2, WEED_LEAF_NEW_DEFAULT);
8975       weed_leaf_copy(in_param2, "host_value_backup", in_param2, WEED_LEAF_VALUE);
8976       weed_leaf_copy(paramtmpl2, WEED_LEAF_NEW_DEFAULT, in_param, WEED_LEAF_VALUE);
8977       fill_param_vals_to(in_param2, paramtmpl2, nvals / vsize);
8978       ign_array = weed_get_boolean_array(in_param2, WEED_LEAF_IGNORE, NULL);
8979       for (int i = 0; i < nvals; i += vsize) {
8980         if (!weed_leaf_elements_equate(in_param2, WEED_LEAF_VALUE, in_param2, "host_value_backup", i))
8981           ign_array[i] = WEED_FALSE;
8982         else
8983           ign_array[i] = WEED_TRUE;
8984       }
8985       weed_set_boolean_array(in_param2, WEED_LEAF_IGNORE, nvals / vsize, ign_array);
8986       weed_leaf_delete(in_param2, "host_value_backup");
8987       weed_leaf_copy(paramtmpl2, WEED_LEAF_NEW_DEFAULT, paramtmpl2, "host_new_def_backup");
8988       weed_leaf_delete(paramtmpl2, "host_new_def_backup");
8989       lives_freep((void **)&ign_array);
8990     } else {
8991       weed_leaf_copy(in_param2, WEED_LEAF_VALUE, in_param, WEED_LEAF_VALUE);
8992       if (key != -1)
8993         filter_mutex_unlock(key);
8994     }
8995   }
8996   return copyto;
8997 }
8998 
8999 
rec_param_change(weed_plant_t * inst,int pnum)9000 void rec_param_change(weed_plant_t *inst, int pnum) {
9001   // should be called with event_list_mutex unlocked !
9002   ticks_t actual_ticks;
9003   weed_plant_t *in_param;
9004   int key;
9005   int error;
9006 
9007   weed_instance_ref(inst);
9008 
9009   // do not record changes for the floating fx dialog box (rte window params)
9010   if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NORECORD)
9011       && weed_get_boolean_value(inst, WEED_LEAF_HOST_NORECORD, &error)) {
9012     weed_instance_unref(inst);
9013     return;
9014   }
9015 
9016   // do not record changes for generators - those get recorded to scrap_file or ascrap_file
9017   if (enabled_in_channels(inst, FALSE) == 0) {
9018     weed_instance_unref(inst);
9019     return;
9020   }
9021 
9022   //actual_ticks = mainw->clock_ticks;//lives_get_current_playback_ticks(mainw->origsecs, mainw->orignsecs, NULL);
9023   actual_ticks = mainw->startticks; ///< use the "thoretical" time
9024 
9025   pthread_mutex_lock(&mainw->event_list_mutex);
9026   key = weed_get_int_value(inst, WEED_LEAF_HOST_KEY, &error);
9027 
9028   in_param = weed_inst_in_param(inst, pnum, FALSE, FALSE);
9029 
9030   mainw->event_list = append_param_change_event(mainw->event_list, actual_ticks, pnum, in_param, init_events[key], pchains[key]);
9031   pthread_mutex_unlock(&mainw->event_list_mutex);
9032 
9033   weed_instance_unref(inst);
9034 }
9035 
9036 #define KEYSCALE 255.
9037 
9038 
weed_set_blend_factor(int hotkey)9039 void weed_set_blend_factor(int hotkey) {
9040   weed_plant_t *inst, *in_param, *paramtmpl;
9041 
9042   LiVESList *list = NULL;
9043 
9044   weed_plant_t **in_params;
9045 
9046   double vald, mind, maxd;
9047 
9048   int vali, mini, maxi;
9049   int param_type, pnum, inc_count;
9050 
9051   if (hotkey < 0) return;
9052 
9053   filter_mutex_lock(hotkey);
9054 
9055   inst = weed_instance_obtain(hotkey, key_modes[hotkey]);
9056 
9057   if (!inst) {
9058     filter_mutex_unlock(hotkey);
9059     return;
9060   }
9061 
9062   pnum = get_nth_simple_param(inst, 0);
9063 
9064   if (pnum == -1)  {
9065     weed_instance_unref(inst);
9066     filter_mutex_unlock(hotkey);
9067     return;
9068   }
9069 
9070   in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
9071   in_param = in_params[pnum];
9072   lives_free(in_params);
9073 
9074   paramtmpl = weed_param_get_template(in_param);
9075   param_type = weed_paramtmpl_get_type(paramtmpl);
9076 
9077   inc_count = enabled_in_channels(inst, FALSE);
9078 
9079   /* filter_mutex_unlock(hotkey); */
9080   /* // record old value */
9081   /* //copyto = set_copy_to(inst, pnum, FALSE); */
9082   /* filter_mutex_lock(hotkey); */
9083 
9084   if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS) && inc_count > 0) {
9085     //pthread_mutex_lock(&mainw->event_list_mutex);
9086     rec_param_change(inst, pnum);
9087     /* if (copyto > -1) { */
9088     /*   rec_param_change(inst, copyto); */
9089     /* } */
9090     //pthread_mutex_unlock(&mainw->event_list_mutex);
9091   }
9092 
9093   if (weed_param_does_wrap(in_param)) {
9094     if (mainw->blend_factor >= 256.) mainw->blend_factor -= 256.;
9095     else if (mainw->blend_factor <= -1.) mainw->blend_factor += 256.;
9096   } else {
9097     if (mainw->blend_factor < 0.) mainw->blend_factor = 0.;
9098     else if (mainw->blend_factor > 255.) mainw->blend_factor = 255.;
9099   }
9100 
9101   switch (param_type) {
9102   case WEED_PARAM_INTEGER:
9103     vali = weed_get_int_value(in_param, WEED_LEAF_VALUE, NULL);
9104     mini = weed_get_int_value(paramtmpl, WEED_LEAF_MIN, NULL);
9105     maxi = weed_get_int_value(paramtmpl, WEED_LEAF_MAX, NULL);
9106 
9107     weed_set_int_value(in_param, WEED_LEAF_VALUE, (int)((double)mini +
9108                        (mainw->blend_factor / KEYSCALE * (double)(maxi - mini)) + .5));
9109 
9110     vali = weed_get_int_value(in_param, WEED_LEAF_VALUE, NULL);
9111 
9112     list = lives_list_append(list, lives_strdup_printf("%d", vali));
9113     list = lives_list_append(list, lives_strdup_printf("%d", mini));
9114     list = lives_list_append(list, lives_strdup_printf("%d", maxi));
9115     update_pwindow(hotkey, pnum, list);
9116     if (mainw->ce_thumbs) ce_thumbs_update_params(hotkey, pnum, list);
9117     lives_list_free_all(&list);
9118 
9119     break;
9120   case WEED_PARAM_FLOAT:
9121     vald = weed_get_double_value(in_param, WEED_LEAF_VALUE, NULL);
9122     mind = weed_get_double_value(paramtmpl, WEED_LEAF_MIN, NULL);
9123     maxd = weed_get_double_value(paramtmpl, WEED_LEAF_MAX, NULL);
9124 
9125     weed_set_double_value(in_param, WEED_LEAF_VALUE, mind + (mainw->blend_factor / KEYSCALE * (maxd - mind)));
9126     vald = weed_get_double_value(in_param, WEED_LEAF_VALUE, NULL);
9127 
9128     list = lives_list_append(list, lives_strdup_printf("%.4f", vald));
9129     list = lives_list_append(list, lives_strdup_printf("%.4f", mind));
9130     list = lives_list_append(list, lives_strdup_printf("%.4f", maxd));
9131     update_pwindow(hotkey, pnum, list);
9132     if (mainw->ce_thumbs) ce_thumbs_update_params(hotkey, pnum, list);
9133     lives_list_free_all(&list);
9134 
9135     break;
9136   case WEED_PARAM_SWITCH:
9137     vali = !!(int)mainw->blend_factor;
9138     weed_set_boolean_value(in_param, WEED_LEAF_VALUE, vali);
9139     vali = weed_get_boolean_value(in_param, WEED_LEAF_VALUE, NULL);
9140     mainw->blend_factor = (double)vali;
9141 
9142     list = lives_list_append(list, lives_strdup_printf("%d", vali));
9143     update_pwindow(hotkey, pnum, list);
9144     if (mainw->ce_thumbs) ce_thumbs_update_params(hotkey, pnum, list);
9145     lives_list_free_all(&list);
9146 
9147     break;
9148   default:
9149     break;
9150   }
9151 
9152   /* filter_mutex_unlock(hotkey); */
9153   /* set_copy_to(inst, pnum, TRUE); */
9154   /* filter_mutex_lock(hotkey); */
9155 
9156   if (mainw->record && !mainw->record_paused && LIVES_IS_PLAYING && (prefs->rec_opts & REC_EFFECTS) && inc_count > 0) {
9157     //pthread_mutex_lock(&mainw->event_list_mutex);
9158     rec_param_change(inst, pnum);
9159     /* if (copyto > -1) { */
9160     /*   rec_param_change(inst, copyto); */
9161     /* } */
9162     //pthread_mutex_unlock(&mainw->event_list_mutex);
9163   }
9164   weed_instance_unref(inst);
9165   filter_mutex_unlock(hotkey);
9166 }
9167 
9168 
weed_get_blend_factor(int hotkey)9169 int weed_get_blend_factor(int hotkey) {
9170   // filter mutex MUST be locked
9171 
9172   weed_plant_t *inst, **in_params, *in_param, *paramtmpl;
9173   int vali, mini, maxi;
9174   double vald, mind, maxd;
9175   int weed_ptype;
9176   int i;
9177 
9178   if (hotkey < 0) return 0;
9179   inst = weed_instance_obtain(hotkey, key_modes[hotkey]);
9180 
9181   if (!inst) return 0;
9182 
9183   i = get_nth_simple_param(inst, 0);
9184 
9185   if (i == -1)  {
9186     weed_instance_unref(inst);
9187     return 0;
9188   }
9189 
9190   in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
9191   in_param = in_params[i];
9192 
9193   paramtmpl = weed_param_get_template(in_param);
9194   weed_ptype = weed_paramtmpl_get_type(paramtmpl);
9195 
9196   switch (weed_ptype) {
9197   case WEED_PARAM_INTEGER:
9198     vali = weed_get_int_value(in_param, WEED_LEAF_VALUE, NULL);
9199     mini = weed_get_int_value(paramtmpl, WEED_LEAF_MIN, NULL);
9200     maxi = weed_get_int_value(paramtmpl, WEED_LEAF_MAX, NULL);
9201     lives_free(in_params);
9202     weed_instance_unref(inst);
9203     return (double)(vali - mini) / (double)(maxi - mini) * KEYSCALE;
9204   case WEED_PARAM_FLOAT:
9205     vald = weed_get_double_value(in_param, WEED_LEAF_VALUE, NULL);
9206     mind = weed_get_double_value(paramtmpl, WEED_LEAF_MIN, NULL);
9207     maxd = weed_get_double_value(paramtmpl, WEED_LEAF_MAX, NULL);
9208     lives_free(in_params);
9209     weed_instance_unref(inst);
9210     return (vald - mind) / (maxd - mind) * KEYSCALE;
9211   case WEED_PARAM_SWITCH:
9212     vali = weed_get_boolean_value(in_param, WEED_LEAF_VALUE, NULL);
9213     lives_free(in_params);
9214     weed_instance_unref(inst);
9215     return vali;
9216   default:
9217     lives_free(in_params);
9218     weed_instance_unref(inst);
9219   }
9220 
9221   return 0;
9222 }
9223 
9224 
get_new_inst_for_keymode(int key,int mode)9225 weed_plant_t *get_new_inst_for_keymode(int key, int mode)  {
9226   // key is 0 based
9227   weed_plant_t *inst;
9228   if ((inst = weed_instance_obtain(key, mode)) != NULL) {
9229     if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_MODE)) {
9230       if (weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL) == key
9231           && weed_get_int_value(inst, WEED_LEAF_HOST_MODE, NULL) == mode) {
9232         return inst;
9233       }
9234     }
9235     weed_instance_unref(inst);
9236   }
9237 
9238   for (int i = FX_KEYS_MAX_VIRTUAL; i < FX_KEYS_MAX; i++) {
9239     if ((inst = weed_instance_obtain(i, key_modes[i])) == NULL) {
9240       continue;
9241     }
9242     if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_MODE)) {
9243       if (weed_get_int_value(inst, WEED_LEAF_HOST_KEY, NULL) == key
9244           && weed_get_int_value(inst, WEED_LEAF_HOST_MODE, NULL) == mode) {
9245         return inst;
9246       }
9247     }
9248     weed_instance_unref(inst);
9249   }
9250 
9251   return NULL;
9252 }
9253 
9254 
9255 ////////////////////////////////////////////////////////////////////////
9256 
weed_instance_get_type(weed_plant_t * inst,boolean getsub)9257 LIVES_INLINE char *weed_instance_get_type(weed_plant_t *inst, boolean getsub) {
9258   // return value should be free'd after use
9259   weed_plant_t *filter = weed_instance_get_filter(inst, TRUE);
9260   return weed_filter_get_type(filter, getsub, TRUE);
9261 }
9262 
9263 
rte_keymode_get_type(int key,int mode)9264 char *rte_keymode_get_type(int key, int mode) {
9265   // return value should be free'd after use
9266   char *type = lives_strdup("");
9267   weed_plant_t *filter, *inst;
9268   int idx;
9269 
9270   key--;
9271   if (!rte_keymode_valid(key + 1, mode, TRUE)) return type;
9272 
9273   if ((idx = key_to_fx[key][mode]) == -1) return type;
9274   if ((filter = weed_filters[idx]) == NULL) return type;
9275 
9276   lives_free(type);
9277 
9278   mainw->osc_block = TRUE;
9279 
9280   if ((inst = key_to_instance[key][mode]) != NULL) {
9281     // return details for instance
9282     type = weed_instance_get_type(inst, TRUE);
9283   } else type = weed_filter_get_type(filter, TRUE, TRUE);
9284 
9285   mainw->osc_block = FALSE;
9286   return type;
9287 }
9288 
9289 
rte_keymode_get_category(int key,int mode)9290 lives_fx_cat_t rte_keymode_get_category(int key, int mode) {
9291   weed_plant_t *filter;
9292   int idx;
9293   lives_fx_cat_t cat;
9294 
9295   key--;
9296   if (!rte_keymode_valid(key + 1, mode, TRUE)) return LIVES_FX_CAT_NONE;
9297 
9298   if ((idx = key_to_fx[key][mode]) == -1) return LIVES_FX_CAT_NONE;
9299   if ((filter = weed_filters[idx]) == NULL) return LIVES_FX_CAT_NONE;
9300 
9301   else cat = weed_filter_categorise(filter,
9302                                       enabled_in_channels(filter, FALSE),
9303                                       enabled_out_channels(filter, FALSE));
9304 
9305   return cat;
9306 }
9307 
9308 
9309 ///////////////////////////////////////////////////////////////////////////////
9310 
get_next_free_key(void)9311 int get_next_free_key(void) {
9312   // 0 based
9313   int i, free_key;
9314   free_key = next_free_key;
9315   for (i = free_key + 1; i < FX_KEYS_MAX; i++) {
9316     if (key_to_fx[i][0] == -1) {
9317       next_free_key = i;
9318       break;
9319     }
9320   }
9321   if (i == FX_KEYS_MAX) next_free_key = -1;
9322   return free_key;
9323 }
9324 
9325 
weed_delete_effectkey(int key,int mode)9326 boolean weed_delete_effectkey(int key, int mode) {
9327   // delete the effect binding for key/mode and move higher numbered slots down
9328   // also moves the active mode if applicable
9329   // returns FALSE if there was no effect bound to key/mode
9330 
9331   char *tmp;
9332 
9333   boolean was_started = FALSE;
9334 
9335   int oldkeymode = key_modes[--key];
9336   int orig_mode = mode;
9337   int modekey = key;
9338 
9339   if (key_to_fx[key][mode] == -1) return FALSE;
9340 
9341   filter_mutex_lock(key);
9342   if (key < FX_KEYS_MAX_VIRTUAL) free_key_defaults(key, mode);
9343 
9344   for (; mode < (key < FX_KEYS_MAX_VIRTUAL ? prefs->max_modes_per_key : 1); mode++) {
9345     mainw->osc_block = TRUE;
9346     if (key >= FX_KEYS_MAX_VIRTUAL || mode == prefs->max_modes_per_key - 1 || key_to_fx[key][mode + 1] == -1) {
9347       if (key_to_instance[key][mode]) {
9348         was_started = TRUE;
9349         if (key_modes[key] == mode) modekey = -key - 1;
9350         else key_modes[key] = mode;
9351         weed_deinit_effect(modekey);
9352         key_modes[key] = oldkeymode;
9353       }
9354 
9355       key_to_fx[key][mode] = -1;
9356 
9357       if (mode == orig_mode && key_modes[key] == mode) {
9358         key_modes[key] = 0;
9359         if (was_started) {
9360           if (key_to_fx[key][0] != -1) {
9361             if (!weed_init_effect(modekey)) {
9362               // TODO
9363               filter_mutex_lock(key);
9364             }
9365           } else {
9366             pthread_mutex_lock(&mainw->event_list_mutex);
9367             if (rte_key_is_enabled(1 + key)) mainw->rte ^= (GU641 << key);
9368             pthread_mutex_unlock(&mainw->event_list_mutex);
9369           }
9370         }
9371       }
9372 
9373       break; // quit the loop
9374     } else if (key < FX_KEYS_MAX_VIRTUAL) {
9375       filter_mutex_unlock(key);
9376       rte_switch_keymode(key + 1, mode, (tmp = make_weed_hashname
9377                                          (key_to_fx[key][mode + 1], TRUE, FALSE, 0, FALSE)));
9378       lives_free(tmp);
9379       filter_mutex_lock(key);
9380       key_defaults[key][mode] = key_defaults[key][mode + 1];
9381       key_defaults[key][mode + 1] = NULL;
9382     }
9383   }
9384 
9385   if (key >= FX_KEYS_MAX_VIRTUAL && key < next_free_key) next_free_key = key;
9386 
9387   mainw->osc_block = FALSE;
9388   if (key_modes[key] > orig_mode) key_modes[key]--;
9389   filter_mutex_unlock(key);
9390 
9391   return TRUE;
9392 }
9393 
9394 
9395 /////////////////////////////////////////////////////////////////////////////
9396 
rte_key_valid(int key,boolean is_userkey)9397 boolean rte_key_valid(int key, boolean is_userkey) {
9398   // key is 1 based
9399   key--;
9400 
9401   if (key < 0 || (is_userkey && key >= FX_KEYS_MAX_VIRTUAL) || key >= FX_KEYS_MAX) return FALSE;
9402   if (key_to_fx[key][key_modes[key]] == -1) return FALSE;
9403   return TRUE;
9404 }
9405 
9406 
rte_keymode_valid(int key,int mode,boolean is_userkey)9407 boolean rte_keymode_valid(int key, int mode, boolean is_userkey) {
9408   // key is 1 based
9409   if (key < 1 || (is_userkey && key > FX_KEYS_MAX_VIRTUAL) || key > FX_KEYS_MAX || mode < 0 ||
9410       mode >= (key < FX_KEYS_MAX_VIRTUAL ? prefs->max_modes_per_key : 1)) return FALSE;
9411   if (key_to_fx[--key][mode] == -1) return FALSE;
9412   return TRUE;
9413 }
9414 
9415 
rte_keymode_get_filter_idx(int key,int mode)9416 int rte_keymode_get_filter_idx(int key, int mode) {
9417   // key is 1 based
9418   if (key < 1 || key > FX_KEYS_MAX || mode < 0 ||
9419       mode >= (key < FX_KEYS_MAX_VIRTUAL ? prefs->max_modes_per_key : 1)) return -1;
9420   return (key_to_fx[--key][mode]);
9421 }
9422 
9423 
rte_key_getmode(int key)9424 int rte_key_getmode(int key) {
9425   // get current active mode for an rte key
9426   // key is 1 based
9427 
9428   if (key < 1 || key > FX_KEYS_MAX) return -1;
9429   return key_modes[--key];
9430 }
9431 
9432 
rte_key_getmaxmode(int key)9433 int rte_key_getmaxmode(int key) {
9434   // gets the highest mode with filter mapped for a key
9435   // not to be confused with rte_get_modespk() which returns the maximum possible
9436 
9437   register int i;
9438 
9439   if (key < 1 || key > FX_KEYS_MAX) return -1;
9440 
9441   key--;
9442 
9443   for (i = 0; i < (key < FX_KEYS_MAX_VIRTUAL ? prefs->max_modes_per_key : 1); i++) {
9444     if (key_to_fx[key][i] == -1) return --i;
9445   }
9446   return --i;
9447 }
9448 
9449 
rte_keymode_get_instance(int key,int mode)9450 weed_plant_t *rte_keymode_get_instance(int key, int mode) {
9451   weed_plant_t *inst;
9452 
9453   key--;
9454   if (!rte_keymode_valid(key + 1, mode, FALSE)) return NULL;
9455   mainw->osc_block = TRUE;
9456   if ((inst = weed_instance_obtain(key, mode)) == NULL) {
9457     mainw->osc_block = FALSE;
9458     return NULL;
9459   }
9460   mainw->osc_block = FALSE;
9461   return inst;
9462 }
9463 
9464 
rte_keymode_get_filter(int key,int mode)9465 weed_plant_t *rte_keymode_get_filter(int key, int mode) {
9466   // key is 1 based
9467   key--;
9468   if (!rte_keymode_valid(key + 1, mode, FALSE)) return NULL;
9469   return weed_filters[key_to_fx[key][mode]];
9470 }
9471 
9472 
9473 #define MAX_AUTHOR_LEN 10
9474 
weed_filter_idx_get_name(int idx,boolean add_subcats,boolean add_notes)9475 char *weed_filter_idx_get_name(int idx, boolean add_subcats, boolean add_notes) {
9476   // return value should be free'd after use
9477   weed_plant_t *filter;
9478   char *filter_name, *tmp;
9479 
9480   if (idx == -1) return lives_strdup("");
9481   if ((filter = weed_filters[idx]) == NULL) return lives_strdup("");
9482 
9483   filter_name = weed_filter_get_name(filter);
9484 
9485   if (add_subcats) {
9486     lives_fx_cat_t cat = weed_filter_categorise(filter,
9487                          enabled_in_channels(filter, TRUE),
9488                          enabled_out_channels(filter, TRUE));
9489     lives_fx_cat_t sub = weed_filter_subcategorise(filter, cat, FALSE);
9490     if (sub != LIVES_FX_CAT_NONE) {
9491       tmp = lives_strdup_printf("%s (%s)", filter_name, lives_fx_cat_to_text(sub, FALSE));
9492       lives_free(filter_name);
9493       filter_name = tmp;
9494     }
9495   }
9496 
9497   if (add_notes) {
9498     // if it's unstable add that
9499     if (weed_filter_hints_unstable(filter)) {
9500       tmp = lives_strdup_printf(_("%s [unstable]"), filter_name);
9501       lives_free(filter_name);
9502       filter_name = tmp;
9503     }
9504   }
9505 
9506   return filter_name;
9507 }
9508 
9509 
weed_filter_idx_get_package_name(int idx)9510 char *weed_filter_idx_get_package_name(int idx) {
9511   // return value should be free'd after use
9512   weed_plant_t *filter;
9513   if (idx == -1) return NULL;
9514   if (!(filter = weed_filters[idx])) return NULL;
9515   return weed_filter_get_package_name(filter);
9516 }
9517 
9518 
weed_instance_get_filter_name(weed_plant_t * inst,boolean get_compound_parent)9519 char *weed_instance_get_filter_name(weed_plant_t *inst, boolean get_compound_parent) {
9520   // return value should be lives_free'd after use
9521   weed_plant_t *filter;
9522   char *filter_name;
9523 
9524   if (!inst) return lives_strdup("");
9525   filter = weed_instance_get_filter(inst, get_compound_parent);
9526   filter_name = weed_get_string_value(filter, WEED_LEAF_NAME, NULL);
9527   return filter_name;
9528 }
9529 
9530 
rte_keymode_get_filter_name(int key,int mode,boolean add_notes)9531 char *rte_keymode_get_filter_name(int key, int mode, boolean add_notes) {
9532   // return value should be lives_free'd after use
9533   // key is 1 based
9534   key--;
9535   if (!rte_keymode_valid(key + 1, mode, TRUE)) return lives_strdup("");
9536   return (weed_filter_idx_get_name(key_to_fx[key][mode], FALSE, add_notes));
9537 }
9538 
9539 
rte_keymode_get_plugin_name(int key,int mode)9540 char *rte_keymode_get_plugin_name(int key, int mode) {
9541   // return value should be lives_free'd after use
9542   // key is 1 based
9543   weed_plant_t *filter, *plugin_info;
9544   char *name;
9545 
9546   key--;
9547   if (!rte_keymode_valid(key + 1, mode, TRUE)) return lives_strdup("");
9548 
9549   filter = weed_filters[key_to_fx[key][mode]];
9550   plugin_info = weed_get_plantptr_value(filter, WEED_LEAF_PLUGIN_INFO, NULL);
9551   name = weed_get_string_value(plugin_info, WEED_LEAF_HOST_PLUGIN_NAME, NULL);
9552   return name;
9553 }
9554 
9555 
rte_bg_gen_key(void)9556 LIVES_GLOBAL_INLINE int rte_bg_gen_key(void) {
9557   return bg_generator_key;
9558 }
9559 
rte_fg_gen_key(void)9560 LIVES_GLOBAL_INLINE int rte_fg_gen_key(void) {
9561   return fg_generator_key;
9562 }
9563 
rte_bg_gen_mode(void)9564 LIVES_GLOBAL_INLINE int rte_bg_gen_mode(void) {
9565   return bg_generator_mode;
9566 }
9567 
rte_fg_gen_mode(void)9568 LIVES_GLOBAL_INLINE int rte_fg_gen_mode(void) {
9569   return fg_generator_mode;
9570 }
9571 
9572 /**
9573    @brief
9574 
9575   for rte textmode, get first string parameter for current key/mode instance
9576   we will then forward all keystrokes to this parm WEED_LEAF_VALUE until the exit key (TAB)
9577   is pressed
9578 */
get_textparm(void)9579 weed_plant_t *get_textparm(void) {
9580   weed_plant_t *inst, **in_params, *ptmpl, *ret;
9581 
9582   int key = mainw->rte_keys, mode, i, ptype;
9583 
9584   if (key == -1) return NULL;
9585 
9586   mode = rte_key_getmode(key + 1);
9587 
9588   if ((inst = weed_instance_obtain(key, mode))) {
9589     int nparms;
9590 
9591     in_params = weed_get_plantptr_array_counted(inst, WEED_LEAF_IN_PARAMETERS, &nparms);
9592     if (nparms == 0) {
9593       weed_instance_unref(inst);
9594       return NULL;
9595     }
9596 
9597     for (i = 0; i < nparms; i++) {
9598       ptmpl = weed_param_get_template(in_params[i]);
9599       ptype = weed_paramtmpl_get_type(ptmpl);
9600 
9601       if (ptype == WEED_PARAM_TEXT) {
9602         ret = in_params[i];
9603         weed_set_int_value(ret, WEED_LEAF_HOST_IDX, i);
9604         weed_set_plantptr_value(ret, WEED_LEAF_HOST_INSTANCE, inst);
9605         lives_free(in_params);
9606         weed_instance_unref(inst);
9607         return ret;
9608       }
9609     }
9610 
9611     lives_free(in_params);
9612     weed_instance_unref(inst);
9613   }
9614 
9615   return NULL;
9616 }
9617 
9618 /**
9619    @brief
9620 
9621   newmode has two special values, -1 = cycle forwards, -2 = cycle backwards
9622   key is 1 based, but may be 0 to use the current mainw->rte_keys
9623   special handling ensures that if we switch transitions, any background generators survive the switchover
9624   call with filter mutex unlocked
9625 */
rte_key_setmode(int key,int newmode)9626 boolean rte_key_setmode(int key, int newmode) {
9627   weed_plant_t *inst, *last_inst;
9628   int oldmode;
9629   int blend_file;
9630   lives_whentostop_t whentostop = mainw->whentostop;
9631   int real_key;
9632 
9633   if (key == 0) {
9634     if ((key = mainw->rte_keys) == -1) return FALSE;
9635   } else key--;
9636 
9637   filter_mutex_lock(key);
9638 
9639   real_key = key;
9640 
9641   oldmode = key_modes[key];
9642 
9643   if (key_to_fx[key][0] == -1) {
9644     filter_mutex_unlock(key);
9645     return FALSE; // nothing is mapped to effect key
9646   }
9647 
9648   if (newmode == -1) {
9649     // cycle forwards
9650     if (oldmode == prefs->max_modes_per_key - 1 || key_to_fx[key][oldmode + 1] == -1) {
9651       newmode = 0;
9652     } else {
9653       newmode = key_modes[key] + 1;
9654     }
9655   }
9656 
9657   if (newmode == -2) {
9658     // cycle backwards
9659     newmode = key_modes[key] - 1;
9660     if (newmode < 0) {
9661       for (newmode = prefs->max_modes_per_key - 1; newmode >= 0; newmode--) {
9662         if (key_to_fx[key][newmode] != -1) break;
9663       }
9664     }
9665   }
9666 
9667   if (newmode < 0 || newmode > rte_key_getmaxmode(key + 1)) {
9668     filter_mutex_unlock(key);
9669     return FALSE;
9670   }
9671 
9672   if (key_to_fx[key][newmode] == -1) {
9673     filter_mutex_unlock(key);
9674     return FALSE;
9675   }
9676 
9677   if (rte_window) rtew_set_mode_radio(key, newmode);
9678   if (mainw->ce_thumbs) ce_thumbs_set_mode_combo(key, newmode);
9679 
9680   mainw->osc_block = TRUE;
9681   mainw->blend_palette = WEED_PALETTE_END;
9682 
9683   // TODO - block template channel changes
9684 
9685   if ((inst = weed_instance_obtain(key, oldmode)) != NULL) {  // adds a ref
9686     if (enabled_in_channels(inst, FALSE) == 2 && enabled_in_channels(weed_filters[key_to_fx[key][newmode]], FALSE) == 2) {
9687       // transition --> transition, allow any bg generators to survive
9688       key = -key - 1;
9689     }
9690   }
9691 
9692   if (oldmode != newmode) {
9693     blend_file = mainw->blend_file;
9694 
9695     if (inst) {
9696       // handle compound fx
9697       last_inst = inst;
9698       while (get_next_compound_inst(last_inst)) last_inst = get_next_compound_inst(last_inst);
9699     }
9700 
9701     if (inst && (enabled_in_channels(inst, FALSE) > 0 || enabled_out_channels(last_inst, FALSE) == 0 ||
9702                  is_pure_audio(inst, FALSE))) {
9703       // not a (video or video/audio) generator
9704       weed_deinit_effect(key);
9705     } else if (enabled_in_channels(weed_filters[key_to_fx[key][newmode]], FALSE) == 0 &&
9706                has_video_chans_out(weed_filters[key_to_fx[key][newmode]], TRUE))
9707       mainw->whentostop = NEVER_STOP; // when gen->gen, dont stop pb
9708 
9709     key_modes[real_key] = newmode;
9710 
9711     mainw->blend_file = blend_file;
9712 
9713     if (inst) {
9714       if (!weed_init_effect(key)) {
9715         weed_instance_unref(inst);
9716         // TODO - unblock template channel changes
9717         mainw->whentostop = whentostop;
9718         key = real_key;
9719         pthread_mutex_lock(&mainw->event_list_mutex);
9720         if (rte_key_is_enabled(1 + key)) mainw->rte ^= (GU641 << key);
9721         pthread_mutex_unlock(&mainw->event_list_mutex);
9722         mainw->osc_block = FALSE;
9723         return FALSE;
9724       }
9725       weed_instance_unref(inst);
9726       if (mainw->ce_thumbs) ce_thumbs_add_param_box(real_key, TRUE);
9727     }
9728     // TODO - unblock template channel changes
9729     mainw->whentostop = whentostop;
9730   }
9731 
9732   filter_mutex_unlock(real_key);
9733   mainw->osc_block = FALSE;
9734   return TRUE;
9735 }
9736 
9737 
9738 /**
9739    @brief
9740 
9741   we will add a filter_class at the next free slot for key, and return the slot number
9742   if idx is -1 (probably meaning the filter was not found), we return -1
9743   if all slots are full, we return -3
9744   currently, generators and non-generators cannot be mixed on the same key (causes problems if the mode is switched)
9745   in this case a -2 is returned
9746 */
weed_add_effectkey_by_idx(int key,int idx)9747 int weed_add_effectkey_by_idx(int key, int idx) {
9748   boolean has_gen = FALSE;
9749   boolean has_non_gen = FALSE;
9750 
9751   int i;
9752 
9753   if (idx == -1) return -1;
9754 
9755   key--;
9756 
9757   for (i = 0; i < prefs->max_modes_per_key; i++) {
9758     if (key_to_fx[key][i] != -1) {
9759       if (enabled_in_channels(weed_filters[key_to_fx[key][i]], FALSE) == 0
9760           && has_video_chans_out(weed_filters[key_to_fx[key][i]], TRUE))
9761         has_gen = TRUE;
9762       else has_non_gen = TRUE;
9763     } else {
9764       if ((enabled_in_channels(weed_filters[idx], FALSE) == 0 && has_non_gen &&
9765            !all_outs_alpha(weed_filters[idx], TRUE) && has_video_chans_out(weed_filters[idx], TRUE)) ||
9766           (enabled_in_channels(weed_filters[idx], FALSE) > 0 && has_gen)) return -2;
9767       key_to_fx[key][i] = idx;
9768       if (rte_window && !mainw->is_rendering && !mainw->multitrack) {
9769         // if rte window is visible add to combo box
9770         char *tmp;
9771         rtew_combo_set_text(key, i, (tmp = rte_keymode_get_filter_name(key + 1, i, FALSE)));
9772         lives_free(tmp);
9773 
9774         // set in ce_thumb combos
9775         if (mainw->ce_thumbs) ce_thumbs_reset_combo(key);
9776       }
9777       return i;
9778     }
9779   }
9780   return -3;
9781 }
9782 
9783 
weed_add_effectkey(int key,const char * hashname,boolean fullname)9784 int weed_add_effectkey(int key, const char *hashname, boolean fullname) {
9785   // add a filter_class by hashname to an effect_key
9786   int idx = weed_get_idx_for_hashname(hashname, fullname);
9787   return weed_add_effectkey_by_idx(key, idx);
9788 }
9789 
9790 
rte_switch_keymode(int key,int mode,const char * hashname)9791 int rte_switch_keymode(int key, int mode, const char *hashname) {
9792   // this is called when we switch the filter_class bound to an effect_key/mode
9793   // filter mutex unlocked
9794   weed_plant_t *inst;
9795   int oldkeymode = key_modes[--key];
9796   int id = weed_get_idx_for_hashname(hashname, TRUE), tid;
9797   boolean osc_block;
9798   boolean has_gen = FALSE, has_non_gen = FALSE;
9799 
9800   int test = (mode == 0 ? 1 : 0);
9801 
9802   // effect not found
9803   if (id == -1) return -1;
9804 
9805   filter_mutex_lock(key);
9806 
9807   if ((tid = key_to_fx[key][test]) != -1) {
9808     if (enabled_in_channels(weed_filters[tid], FALSE) == 0 && has_video_chans_out(weed_filters[tid], TRUE)) has_gen = TRUE;
9809     else has_non_gen = TRUE;
9810   }
9811 
9812   if ((enabled_in_channels(weed_filters[id], FALSE) == 0 && has_video_chans_out(weed_filters[id], TRUE) &&
9813        !all_outs_alpha(weed_filters[id], TRUE) && has_non_gen) ||
9814       (enabled_in_channels(weed_filters[id], FALSE) > 0 && has_gen)) {
9815     filter_mutex_unlock(key);
9816     return -2;
9817   }
9818 
9819   osc_block = mainw->osc_block;
9820   mainw->osc_block = TRUE;
9821 
9822   // must be done before switching the key_to_fx, as we need to know number of in_parameter_templates
9823   if (key_defaults[key][mode]) free_key_defaults(key, mode);
9824 
9825   if ((inst = weed_instance_obtain(key, mode)) != NULL) {
9826     key_modes[key] = mode;
9827     weed_deinit_effect(-key - 1); // set is_modeswitch
9828     key_to_fx[key][mode] = id;
9829     if (!weed_init_effect(-key - 1)) {
9830       // TODO
9831       filter_mutex_lock(key);
9832     }
9833     key_modes[key] = oldkeymode;
9834     weed_instance_unref(inst);
9835   } else key_to_fx[key][mode] = id;
9836 
9837   filter_mutex_unlock(key);
9838   mainw->osc_block = osc_block;
9839 
9840   return 0;
9841 }
9842 
9843 
rte_swap_fg_bg(void)9844 void rte_swap_fg_bg(void) {
9845   int key = fg_generator_key;
9846   int mode = fg_generator_mode;
9847   mainw->blend_palette = WEED_PALETTE_END;
9848 
9849   if (key != -1) {
9850     fg_generator_clip = -1;
9851   }
9852   fg_generator_key = bg_generator_key;
9853   fg_generator_mode = bg_generator_mode;
9854   if (fg_generator_key != -1) {
9855     fg_generator_clip = mainw->current_file;
9856   }
9857   bg_generator_key = key;
9858   bg_generator_mode = mode;
9859 }
9860 
9861 
weed_get_sorted_filter(int i)9862 LIVES_GLOBAL_INLINE int weed_get_sorted_filter(int i) {return LIVES_POINTER_TO_INT(lives_list_nth_data(weed_fx_sorted_list, i));}
9863 
9864 
weed_get_all_names(lives_fx_list_t list_type)9865 LiVESList *weed_get_all_names(lives_fx_list_t list_type) {
9866   // remember to free list (list + data)  after use, if non-NULL
9867   LiVESList *list = NULL;
9868   char *filter_name, *hashname, *string;
9869   int i, error;
9870 
9871   for (i = 0; i < num_weed_filters; i++) {
9872     int sorted = weed_get_sorted_filter(i);
9873     weed_plant_t *filter = weed_filters[sorted];
9874     filter_name = weed_get_string_value(filter, WEED_LEAF_NAME, &error);
9875     switch (list_type) {
9876     case FX_LIST_NAME:
9877       // just name
9878       string = lives_strdup(filter_name);
9879       list = lives_list_append(list, (livespointer)string);
9880       break;
9881     case FX_LIST_EXTENDED_NAME: {
9882       // name + author (if dupe) + subcat + observations
9883       string = weed_filter_idx_get_name(sorted, TRUE, TRUE);
9884       list = lives_list_append(list, (livespointer)string);
9885     }
9886     break;
9887     case FX_LIST_HASHNAME:
9888       // hashnames - authors and not extra_authors
9889       hashname = lives_strdup(hashnames[sorted][0].string);
9890       list = lives_list_append(list, (livespointer)hashname);
9891       break;
9892     }
9893     lives_free(filter_name);
9894   }
9895   return list;
9896 }
9897 
9898 
rte_get_numfilters(void)9899 int rte_get_numfilters(void) {return num_weed_filters;}
9900 
9901 
9902 ///////////////////
9903 // parameter interpolation
9904 
9905 /**
9906    @brief
9907 
9908   for a multi valued parameter or pchange, we will fill WEED_LEAF_VALUE up to element index with WEED_LEAF_NEW_DEFAULT
9909   we will also create the ignore array if there is not one. The values of this are set to WEED_TRUE if the user
9910   updates the values for that channel, so we know which values to set for interpolation later.
9911   paramtmpl must be supplied, since pchanges do not have one directly
9912 */
fill_param_vals_to(weed_plant_t * param,weed_plant_t * paramtmpl,int index)9913 void fill_param_vals_to(weed_plant_t *param, weed_plant_t *paramtmpl, int index) {
9914   int i, ptype;
9915   int num_vals = weed_leaf_num_elements(param, WEED_LEAF_VALUE);
9916   int new_defi, *valis, *nvalis;
9917   double new_defd, *valds, *nvalds;
9918   char *new_defs, **valss, **nvalss;
9919   int cspace;
9920   int *colsis, *coli;
9921   int vcount;
9922   double *colsds, *cold;
9923 
9924   ptype = weed_paramtmpl_get_type(paramtmpl);
9925   vcount = num_vals;
9926   if (index >= vcount) vcount = ++index;
9927 
9928   switch (ptype) {
9929   case WEED_PARAM_INTEGER:
9930     if (vcount > num_vals) {
9931       new_defi = weed_get_int_value(paramtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
9932       valis = weed_get_int_array(param, WEED_LEAF_VALUE, NULL);
9933       nvalis = (int *)lives_malloc(vcount * sizint);
9934       for (i = 0; i < vcount; i++) {
9935         if (i < num_vals && i < index) nvalis[i] = valis[i];
9936         else if (i <= num_vals && i > index) nvalis[i] = valis[i - 1];
9937         else nvalis[i] = new_defi;
9938       }
9939       weed_set_int_array(param, WEED_LEAF_VALUE, vcount, nvalis);
9940       lives_freep((void **)&valis);
9941       lives_freep((void **)&nvalis);
9942     }
9943     break;
9944   case WEED_PARAM_FLOAT:
9945     if (vcount > num_vals) {
9946       new_defd = weed_get_double_value(paramtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
9947       valds = weed_get_double_array(param, WEED_LEAF_VALUE, NULL);
9948       nvalds = (double *)lives_malloc(vcount * sizdbl);
9949       for (i = 0; i < vcount; i++) {
9950         if (i < num_vals && i < index) nvalds[i] = valds[i];
9951         else if (i <= num_vals && i > index) nvalds[i] = valds[i - 1];
9952         else nvalds[i] = new_defd;
9953       }
9954       weed_set_double_array(param, WEED_LEAF_VALUE, vcount, nvalds);
9955 
9956       lives_freep((void **)&valds);
9957       lives_freep((void **)&nvalds);
9958     }
9959     break;
9960   case WEED_PARAM_SWITCH:
9961     if (vcount > num_vals) {
9962       new_defi = weed_get_boolean_value(paramtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
9963       valis = weed_get_boolean_array(param, WEED_LEAF_VALUE, NULL);
9964       nvalis = (int *)lives_malloc(vcount * sizint);
9965       for (i = 0; i < vcount; i++) {
9966         if (i < num_vals && i < index) nvalis[i] = valis[i];
9967         else if (i <= num_vals && i > index) nvalis[i] = valis[i - 1];
9968         else nvalis[i] = new_defi;
9969       }
9970       weed_set_boolean_array(param, WEED_LEAF_VALUE, vcount, nvalis);
9971       lives_freep((void **)&valis);
9972       lives_freep((void **)&nvalis);
9973     }
9974     break;
9975   case WEED_PARAM_TEXT:
9976     if (vcount > num_vals) {
9977       new_defs = weed_get_string_value(paramtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
9978       valss = weed_get_string_array(param, WEED_LEAF_VALUE, NULL);
9979       nvalss = (char **)lives_malloc(vcount * sizeof(char *));
9980       for (i = 0; i < vcount; i++) {
9981         if (i < num_vals && i < index) nvalss[i] = valss[i];
9982         else if (i <= num_vals && i > index) nvalss[i] = valss[i - 1];
9983         else nvalss[i] = new_defs;
9984       }
9985       weed_set_string_array(param, WEED_LEAF_VALUE, vcount, nvalss);
9986 
9987       for (i = 0; i < index; i++) {
9988         lives_freep((void **)&nvalss[i]);
9989       }
9990 
9991       lives_freep((void **)&valss);
9992       lives_freep((void **)&nvalss);
9993     }
9994     break;
9995   case WEED_PARAM_COLOR:
9996     cspace = weed_get_int_value(paramtmpl, WEED_LEAF_COLORSPACE, NULL);
9997     switch (cspace) {
9998     case WEED_COLORSPACE_RGB:
9999       num_vals /= 3;
10000       vcount = num_vals;
10001       index--;
10002       if (index >= vcount) vcount = ++index;
10003       vcount *= 3;
10004       if (weed_leaf_seed_type(paramtmpl, WEED_LEAF_NEW_DEFAULT) == WEED_SEED_INT) {
10005         colsis = weed_get_int_array(param, WEED_LEAF_VALUE, NULL);
10006         if (weed_leaf_num_elements(paramtmpl, WEED_LEAF_NEW_DEFAULT) == 1) {
10007           coli = (int *)lives_malloc(3 * sizint);
10008           coli[0] = coli[1] = coli[2] = weed_get_int_value(paramtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
10009         } else coli = weed_get_int_array(paramtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
10010         valis = weed_get_int_array(param, WEED_LEAF_VALUE, NULL);
10011         nvalis = (int *)lives_malloc(vcount * sizint);
10012         for (i = 0; i < vcount; i += 3) {
10013           if (i < num_vals && i < index) {
10014             nvalis[i] = valis[i];
10015             nvalis[i + 1] = valis[i + 1];
10016             nvalis[i + 2] = valis[i + 2];
10017           } else if (i <= num_vals && i > index) {
10018             nvalis[i] = valis[i - 3];
10019             nvalis[i + 1] = valis[i - 2];
10020             nvalis[i + 2] = valis[i - 1];
10021           } else {
10022             nvalis[i] = coli[0];
10023             nvalis[i + 1] = coli[1];
10024             nvalis[i + 2] = coli[2];
10025           }
10026         }
10027         weed_set_int_array(param, WEED_LEAF_VALUE, vcount, nvalis);
10028         lives_freep((void **)&valis);
10029         lives_freep((void **)&colsis);
10030         lives_freep((void **)&nvalis);
10031       } else {
10032         colsds = weed_get_double_array(param, WEED_LEAF_VALUE, NULL);
10033         if (weed_leaf_num_elements(paramtmpl, WEED_LEAF_NEW_DEFAULT) == 1) {
10034           cold = (double *)lives_malloc(3 * sizdbl);
10035           cold[0] = cold[1] = cold[2] = weed_get_double_value(paramtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
10036         } else cold = weed_get_double_array(paramtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
10037         valds = weed_get_double_array(param, WEED_LEAF_VALUE, NULL);
10038         nvalds = (double *)lives_malloc(vcount * sizdbl);
10039         for (i = 0; i < vcount; i += 3) {
10040           if (i < num_vals && i < index) {
10041             nvalds[i] = valds[i];
10042             nvalds[i + 1] = valds[i + 1];
10043             nvalds[i + 2] = valds[i + 2];
10044           } else if (i <= num_vals && i > index) {
10045             nvalds[i] = valds[i - 3];
10046             nvalds[i + 1] = valds[i - 2];
10047             nvalds[i + 2] = valds[i - 1];
10048           } else {
10049             nvalds[i] = cold[0];
10050             nvalds[i + 1] = cold[1];
10051             nvalds[i + 2] = cold[2];
10052           }
10053         }
10054         weed_set_double_array(param, WEED_LEAF_VALUE, vcount, nvalds);
10055         lives_freep((void **)&valds);
10056         lives_freep((void **)&colsds);
10057         lives_freep((void **)&nvalds);
10058       }
10059       vcount /= 3;
10060     }
10061     break;
10062   }
10063 
10064   if (is_perchannel_multiw(param)) {
10065     int num_vals;
10066     int *ign_array = weed_get_boolean_array_counted(param, WEED_LEAF_IGNORE, &num_vals);
10067     if (vcount > num_vals) {
10068       ign_array = (int *)lives_realloc(ign_array, vcount * sizint);
10069       for (i = num_vals; i < vcount; i++) {
10070         ign_array[i] = WEED_TRUE;
10071       }
10072       weed_set_boolean_array(param, WEED_LEAF_IGNORE, vcount, ign_array);
10073     }
10074     lives_freep((void **)&ign_array);
10075   }
10076 }
10077 
10078 
get_default_element_int(weed_plant_t * param,int idx,int mpy,int add)10079 static int get_default_element_int(weed_plant_t *param, int idx, int mpy, int add) {
10080   int *valsi, val;
10081   int error;
10082   weed_plant_t *ptmpl = weed_get_plantptr_value(param, WEED_LEAF_TEMPLATE, &error);
10083 
10084   if (!weed_paramtmpl_value_irrelevant(ptmpl) && weed_plant_has_leaf(ptmpl, WEED_LEAF_HOST_DEFAULT) &&
10085       weed_leaf_num_elements(ptmpl, WEED_LEAF_HOST_DEFAULT) > idx * mpy + add) {
10086     valsi = weed_get_int_array(ptmpl, WEED_LEAF_HOST_DEFAULT, &error);
10087     val = valsi[idx * mpy + add];
10088     lives_free(valsi);
10089     return val;
10090   }
10091   if (weed_plant_has_leaf(ptmpl, WEED_LEAF_DEFAULT)
10092       && weed_leaf_num_elements(ptmpl, WEED_LEAF_DEFAULT) > idx * mpy + add) {
10093     valsi = weed_get_int_array(ptmpl, WEED_LEAF_DEFAULT, &error);
10094     val = valsi[idx * mpy + add];
10095     lives_free(valsi);
10096     return val;
10097   }
10098   if (weed_leaf_num_elements(ptmpl, WEED_LEAF_NEW_DEFAULT) == mpy) {
10099     valsi = weed_get_int_array(ptmpl, WEED_LEAF_DEFAULT, &error);
10100     val = valsi[add];
10101     lives_free(valsi);
10102     return val;
10103   }
10104   return weed_get_int_value(ptmpl, WEED_LEAF_NEW_DEFAULT, &error);
10105 }
10106 
10107 
get_default_element_double(weed_plant_t * param,int idx,int mpy,int add)10108 static double get_default_element_double(weed_plant_t *param, int idx, int mpy, int add) {
10109   double *valsd, val;
10110   int error;
10111   weed_plant_t *ptmpl = weed_get_plantptr_value(param, WEED_LEAF_TEMPLATE, &error);
10112 
10113   if (!weed_paramtmpl_value_irrelevant(ptmpl) && weed_plant_has_leaf(ptmpl, WEED_LEAF_HOST_DEFAULT) &&
10114       weed_leaf_num_elements(ptmpl, WEED_LEAF_HOST_DEFAULT) > idx * mpy + add) {
10115     valsd = weed_get_double_array(ptmpl, WEED_LEAF_HOST_DEFAULT, &error);
10116     val = valsd[idx * mpy + add];
10117     lives_free(valsd);
10118     return val;
10119   }
10120   if (weed_plant_has_leaf(ptmpl, WEED_LEAF_DEFAULT)
10121       && weed_leaf_num_elements(ptmpl, WEED_LEAF_DEFAULT) > idx * mpy + add) {
10122     valsd = weed_get_double_array(ptmpl, WEED_LEAF_DEFAULT, &error);
10123     val = valsd[idx * mpy + add];
10124     lives_free(valsd);
10125     return val;
10126   }
10127   if (weed_leaf_num_elements(ptmpl, WEED_LEAF_NEW_DEFAULT) == mpy) {
10128     valsd = weed_get_double_array(ptmpl, WEED_LEAF_DEFAULT, &error);
10129     val = valsd[add];
10130     lives_free(valsd);
10131     return val;
10132   }
10133   return weed_get_double_value(ptmpl, WEED_LEAF_NEW_DEFAULT, &error);
10134 }
10135 
10136 
get_default_element_bool(weed_plant_t * param,int idx)10137 static int get_default_element_bool(weed_plant_t *param, int idx) {
10138   int *valsi, val;
10139   int error;
10140   weed_plant_t *ptmpl = weed_get_plantptr_value(param, WEED_LEAF_TEMPLATE, &error);
10141 
10142   if (!weed_paramtmpl_value_irrelevant(ptmpl) && weed_plant_has_leaf(ptmpl, WEED_LEAF_HOST_DEFAULT) &&
10143       weed_leaf_num_elements(ptmpl, WEED_LEAF_HOST_DEFAULT) > idx) {
10144     valsi = weed_get_boolean_array(ptmpl, WEED_LEAF_HOST_DEFAULT, &error);
10145     val = valsi[idx];
10146     lives_free(valsi);
10147     return val;
10148   }
10149   if (weed_plant_has_leaf(ptmpl, WEED_LEAF_DEFAULT) && weed_leaf_num_elements(ptmpl, WEED_LEAF_DEFAULT) > idx) {
10150     valsi = weed_get_boolean_array(ptmpl, WEED_LEAF_DEFAULT, &error);
10151     val = valsi[idx];
10152     lives_free(valsi);
10153     return val;
10154   }
10155   return weed_get_boolean_value(ptmpl, WEED_LEAF_NEW_DEFAULT, &error);
10156 }
10157 
10158 
get_default_element_string(weed_plant_t * param,int idx)10159 static char *get_default_element_string(weed_plant_t *param, int idx) {
10160   char **valss, *val, *val2;
10161   int error, i;
10162   int numvals;
10163   weed_plant_t *ptmpl = weed_get_plantptr_value(param, WEED_LEAF_TEMPLATE, &error);
10164 
10165   if (!weed_paramtmpl_value_irrelevant(ptmpl) && weed_plant_has_leaf(ptmpl, WEED_LEAF_HOST_DEFAULT) &&
10166       (numvals = weed_leaf_num_elements(ptmpl, WEED_LEAF_HOST_DEFAULT)) > idx) {
10167     valss = weed_get_string_array(ptmpl, WEED_LEAF_HOST_DEFAULT, &error);
10168     val = lives_strdup(valss[idx]);
10169     for (i = 0; i < numvals; i++) lives_free(valss[i]);
10170     lives_free(valss);
10171     return val;
10172   }
10173   if (weed_plant_has_leaf(ptmpl, WEED_LEAF_DEFAULT) && (numvals = weed_leaf_num_elements(ptmpl, WEED_LEAF_DEFAULT)) > idx) {
10174     valss = weed_get_string_array(ptmpl, WEED_LEAF_DEFAULT, &error);
10175     val = lives_strdup(valss[idx]);
10176     for (i = 0; i < numvals; i++) lives_free(valss[i]);
10177     lives_free(valss);
10178     return val;
10179   }
10180   val = weed_get_string_value(ptmpl, WEED_LEAF_NEW_DEFAULT, &error);
10181   val2 = lives_strdup(val);
10182   lives_free(val);
10183   return val2;
10184 }
10185 
10186 
interpolate_param(weed_plant_t * param,void * pchain,weed_timecode_t tc)10187 boolean interpolate_param(weed_plant_t *param, void *pchain, weed_timecode_t tc) {
10188   // return FALSE if param has no "value"
10189   // - this can happen during realtime audio processing, if the effect is inited, but no "value" has been set yet
10190   // filter_mutex should be locked for the key during realtime processing
10191 
10192   weed_plant_t *pchange = (weed_plant_t *)pchain, *last_pchange = NULL;
10193   weed_plant_t *wtmpl;
10194   weed_timecode_t tc_diff = 0, tc_diff2;
10195   void **lpc, **npc;
10196   char **valss, **nvalss;
10197   double *last_valuesd, *next_valuesd;
10198   double *valds = NULL, *nvalds, last_valued;
10199   double last_valuedr, last_valuedg, last_valuedb, last_valueda;
10200   int *last_valuesi, *next_valuesi;
10201   int *valis = NULL, *nvalis, last_valuei;
10202   int last_valueir, last_valueig, last_valueib, last_valueia;
10203   int *ign, num_ign = 0;
10204   int ptype, cspace = 0;
10205   int got_npc, num_values, xnum, num_pvals, k, j;
10206 
10207   if (!pchange) return TRUE;
10208   if ((num_values = weed_leaf_num_elements(param, WEED_LEAF_VALUE)) == 0) return FALSE;
10209   if (weed_param_value_irrelevant(param)) return TRUE;
10210 
10211   while (pchange && get_event_timecode(pchange) <= tc) {
10212     last_pchange = pchange;
10213     pchange = (weed_plant_t *)weed_get_voidptr_value(pchange, WEED_LEAF_NEXT_CHANGE, NULL);
10214   }
10215 
10216   wtmpl = weed_param_get_template(param);
10217 
10218   if ((num_pvals = weed_leaf_num_elements((weed_plant_t *)pchain, WEED_LEAF_VALUE)) > num_values)
10219     num_values = num_pvals; // init a multivalued param
10220 
10221   lpc = (void **)lives_calloc(num_values, sizeof(void *));
10222   npc = (void **)lives_calloc(num_values, sizeof(void *));
10223 
10224   if (num_values == 1) {
10225     lpc[0] = last_pchange;
10226     npc[0] = pchange;
10227   } else {
10228     pchange = (weed_plant_t *)pchain;
10229 
10230     while (pchange) {
10231       num_pvals = weed_leaf_num_elements(pchange, WEED_LEAF_VALUE);
10232       if (num_pvals > num_values) num_pvals = num_values;
10233       if (weed_plant_has_leaf(pchange, WEED_LEAF_IGNORE)) {
10234         ign = weed_get_boolean_array_counted(pchange, WEED_LEAF_IGNORE, &num_ign);
10235       } else ign = NULL;
10236       if (get_event_timecode(pchange) <= tc) {
10237         for (j = 0; j < num_pvals; j++) if (!ign || j >= num_ign || ign[j] == WEED_FALSE) lpc[j] = pchange;
10238       } else {
10239         for (j = 0; j < num_pvals; j++) {
10240           if (!npc[j] && (!ign || j >= num_ign || ign[j] == WEED_FALSE)) npc[j] = pchange;
10241         }
10242         got_npc = 0;
10243         for (j = 0; j < num_values; j++) {
10244           if (npc[j]) got_npc++;
10245         }
10246         if (got_npc == num_values) {
10247           lives_freep((void **)&ign);
10248           break;
10249         }
10250       }
10251       pchange = (weed_plant_t *)weed_get_voidptr_value(pchange, WEED_LEAF_NEXT_CHANGE, NULL);
10252       lives_freep((void **)&ign);
10253     }
10254   }
10255 
10256   ptype = weed_paramtmpl_get_type(wtmpl);
10257   switch (ptype) {
10258   case WEED_PARAM_FLOAT:
10259     valds = (double *)lives_malloc(num_values * (sizeof(double)));
10260     break;
10261   case WEED_PARAM_COLOR:
10262     cspace = weed_get_int_value(wtmpl, WEED_LEAF_COLORSPACE, NULL);
10263     switch (cspace) {
10264     case WEED_COLORSPACE_RGB:
10265       if (!(num_values & 3)) return TRUE;
10266       if (weed_leaf_seed_type(wtmpl, WEED_LEAF_DEFAULT) == WEED_SEED_INT) {
10267         valis = (int *)lives_malloc(num_values * sizint);
10268       } else {
10269         valds = (double *)lives_malloc(num_values * (sizeof(double)));
10270       }
10271       break;
10272     case WEED_COLORSPACE_RGBA:
10273       if (num_values & 3) return TRUE;
10274       if (weed_leaf_seed_type(wtmpl, WEED_LEAF_DEFAULT) == WEED_SEED_INT) {
10275         valis = (int *)lives_malloc(num_values * sizint);
10276       } else {
10277         valds = (double *)lives_malloc(num_values * (sizeof(double)));
10278       }
10279       break;
10280     }
10281     break;
10282   case WEED_PARAM_SWITCH:
10283   case WEED_PARAM_INTEGER:
10284     valis = (int *)lives_malloc(num_values * sizint);
10285     break;
10286   }
10287 
10288   for (j = 0; j < num_values; j++) {
10289     // must interpolate - we use linear interpolation
10290     if (!lpc[j] && !npc[j]) continue;
10291     if (lpc[j] && npc[j]) tc_diff = weed_get_int64_value((weed_plant_t *)npc[j], WEED_LEAF_TIMECODE, NULL) -
10292                                       weed_get_int64_value((weed_plant_t *)lpc[j], WEED_LEAF_TIMECODE, NULL);
10293     switch (ptype) {
10294     case WEED_PARAM_FLOAT:
10295       if (!lpc[j]) {
10296         // before first change
10297         valds[j] = get_default_element_double(param, j, 1, 0);
10298         continue;
10299       }
10300       if (!npc[j]) {
10301         // after last change
10302         xnum = weed_leaf_num_elements((weed_plant_t *)lpc[j], WEED_LEAF_VALUE);
10303         if (xnum > j) {
10304           nvalds = weed_get_double_array((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, NULL);
10305           valds[j] = nvalds[j];
10306           lives_free(nvalds);
10307         } else valds[j] = get_default_element_double(param, j, 1, 0);
10308         continue;
10309       }
10310 
10311       next_valuesd = weed_get_double_array((weed_plant_t *)npc[j], WEED_LEAF_VALUE, NULL);
10312       last_valuesd = weed_get_double_array_counted((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, &xnum);
10313       if (xnum > j) last_valued = last_valuesd[j];
10314       else last_valued = get_default_element_double(param, j, 1, 0);
10315 
10316       valds[j] = last_valued + (double)(next_valuesd[j] - last_valued) / (double)(tc_diff / TICKS_PER_SECOND_DBL) *
10317                  (double)((tc - weed_get_int64_value((weed_plant_t *)lpc[j], WEED_LEAF_TIMECODE, NULL)) / TICKS_PER_SECOND_DBL);
10318 
10319       lives_free(last_valuesd);
10320       lives_free(next_valuesd);
10321       break;
10322     case WEED_PARAM_COLOR:
10323       if (num_values != weed_leaf_num_elements(last_pchange, WEED_LEAF_VALUE)) break; // no interp possible
10324 
10325       switch (cspace) {
10326       case WEED_COLORSPACE_RGB:
10327         k = j * 3;
10328         if (weed_leaf_seed_type(wtmpl, WEED_LEAF_DEFAULT) == WEED_SEED_INT) {
10329           if (!lpc[j]) {
10330             // before first change
10331             valis[k] = get_default_element_int(param, j, 3, 0);
10332             valis[k + 1] = get_default_element_int(param, j, 3, 1);
10333             valis[k + 2] = get_default_element_int(param, j, 3, 2);
10334             j += 3;
10335             continue;
10336           }
10337           if (!npc[j]) {
10338             // after last change
10339             xnum = weed_leaf_num_elements((weed_plant_t *)lpc[j], WEED_LEAF_VALUE);
10340             if (xnum > k) {
10341               nvalis = weed_get_int_array((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, NULL);
10342               valis[k] = nvalis[k];
10343               valis[k + 1] = nvalis[k + 1];
10344               valis[k + 2] = nvalis[k + 2];
10345               lives_free(nvalis);
10346             } else {
10347               valis[k] = get_default_element_int(param, j, 3, 0);
10348               valis[k + 1] = get_default_element_int(param, j, 3, 1);
10349               valis[k + 2] = get_default_element_int(param, j, 3, 2);
10350             }
10351             j += 3;
10352             continue;
10353           }
10354 
10355           next_valuesi = weed_get_int_array((weed_plant_t *)npc[j], WEED_LEAF_VALUE, NULL);
10356           last_valuesi = weed_get_int_array_counted((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, &xnum);
10357           if (xnum > k) {
10358             last_valueir = last_valuesi[k];
10359             last_valueig = last_valuesi[k + 1];
10360             last_valueib = last_valuesi[k + 2];
10361           } else {
10362             last_valueir = get_default_element_int(param, j, 3, 0);
10363             last_valueig = get_default_element_int(param, j, 3, 1);
10364             last_valueib = get_default_element_int(param, j, 3, 2);
10365           }
10366 
10367           if (!next_valuesi) continue; // can happen if we recorded a param change
10368 
10369           valis[k] = last_valueir + (next_valuesi[k] - last_valueir) / (tc_diff / TICKS_PER_SECOND_DBL) *
10370                      ((tc_diff2 = (tc - weed_get_int64_value((weed_plant_t *)lpc[j],
10371                                    WEED_LEAF_TIMECODE, NULL))) / TICKS_PER_SECOND_DBL) + .5;
10372           valis[k + 1] = last_valueig + (next_valuesi[k + 1] - last_valueig) / (tc_diff / TICKS_PER_SECOND_DBL) *
10373                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10374           valis[k + 2] = last_valueib + (next_valuesi[k + 2] - last_valueib) / (tc_diff / TICKS_PER_SECOND_DBL) *
10375                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10376 
10377           lives_free(last_valuesi);
10378           lives_free(next_valuesi);
10379         } else {
10380           if (!lpc[j]) {
10381             // before first change
10382             valds[k] = get_default_element_double(param, j, 3, 0);
10383             valds[k + 1] = get_default_element_double(param, j, 3, 1);
10384             valds[k + 2] = get_default_element_double(param, j, 3, 2);
10385             j += 3;
10386             continue;
10387           }
10388           if (!npc[j]) {
10389             // after last change
10390             xnum = weed_leaf_num_elements((weed_plant_t *)lpc[j], WEED_LEAF_VALUE);
10391             if (xnum > k) {
10392               nvalds = weed_get_double_array((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, NULL);
10393               valds[k] = nvalds[k];
10394               valds[k + 1] = nvalds[k + 1];
10395               valds[k + 2] = nvalds[k + 2];
10396               lives_free(nvalds);
10397             } else {
10398               valds[k] = get_default_element_double(param, j, 3, 0);
10399               valds[k + 1] = get_default_element_double(param, j, 3, 1);
10400               valds[k + 2] = get_default_element_double(param, j, 3, 2);
10401             }
10402             j += 3;
10403             continue;
10404           }
10405 
10406           next_valuesd = weed_get_double_array((weed_plant_t *)npc[j], WEED_LEAF_VALUE, NULL);
10407           last_valuesd = weed_get_double_array_counted((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, &xnum);
10408           if (xnum > k) {
10409             last_valuedr = last_valuesd[k];
10410             last_valuedg = last_valuesd[k + 1];
10411             last_valuedb = last_valuesd[k + 2];
10412           } else {
10413             last_valuedr = get_default_element_double(param, j, 3, 0);
10414             last_valuedg = get_default_element_double(param, j, 3, 1);
10415             last_valuedb = get_default_element_double(param, j, 3, 2);
10416           }
10417           valds[k] = last_valuedr + (next_valuesd[k] - last_valuedr) / (tc_diff / TICKS_PER_SECOND_DBL) *
10418                      ((tc_diff2 = (tc - weed_get_int64_value((weed_plant_t *)lpc[j],
10419                                    WEED_LEAF_TIMECODE, NULL))) / TICKS_PER_SECOND_DBL);
10420           valds[k + 1] = last_valuedg + (next_valuesd[k + 1] - last_valuedg) / (tc_diff / TICKS_PER_SECOND_DBL) *
10421                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10422           valds[k + 2] = last_valuedb + (next_valuesd[k + 2] - last_valuedb) / (tc_diff / TICKS_PER_SECOND_DBL) *
10423                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10424 
10425           lives_free(last_valuesd);
10426           lives_free(next_valuesd);
10427         }
10428         j += 3;
10429         break;
10430       case WEED_COLORSPACE_RGBA:
10431         k = j * 4;
10432         if (weed_leaf_seed_type(wtmpl, WEED_LEAF_DEFAULT) == WEED_SEED_INT) {
10433           if (!lpc[j]) {
10434             // before first change
10435             valis[k] = get_default_element_int(param, j, 4, 0);
10436             valis[k + 1] = get_default_element_int(param, j, 4, 1);
10437             valis[k + 2] = get_default_element_int(param, j, 4, 2);
10438             valis[k + 3] = get_default_element_int(param, j, 4, 3);
10439             j += 4;
10440             continue;
10441           }
10442           if (!npc[j]) {
10443             // after last change
10444             xnum = weed_leaf_num_elements((weed_plant_t *)lpc[j], WEED_LEAF_VALUE);
10445             if (xnum > k) {
10446               nvalis = weed_get_int_array((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, NULL);
10447               valis[k] = nvalis[k];
10448               valis[k + 1] = nvalis[k + 1];
10449               valis[k + 2] = nvalis[k + 2];
10450               valis[k + 3] = nvalis[k + 3];
10451               lives_free(nvalis);
10452             } else {
10453               valis[k] = get_default_element_int(param, j, 4, 0);
10454               valis[k + 1] = get_default_element_int(param, j, 4, 1);
10455               valis[k + 2] = get_default_element_int(param, j, 4, 2);
10456               valis[k + 3] = get_default_element_int(param, j, 4, 3);
10457             }
10458             j += 4;
10459             continue;
10460           }
10461 
10462           next_valuesi = weed_get_int_array((weed_plant_t *)npc[j], WEED_LEAF_VALUE, NULL);
10463           last_valuesi = weed_get_int_array_counted((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, &xnum);
10464           if (xnum > k) {
10465             last_valueir = last_valuesi[k];
10466             last_valueig = last_valuesi[k + 1];
10467             last_valueib = last_valuesi[k + 2];
10468             last_valueia = last_valuesi[k + 3];
10469           } else {
10470             last_valueir = get_default_element_int(param, j, 4, 0);
10471             last_valueig = get_default_element_int(param, j, 4, 1);
10472             last_valueib = get_default_element_int(param, j, 4, 2);
10473             last_valueia = get_default_element_int(param, j, 4, 3);
10474           }
10475 
10476           if (!next_valuesi) continue; // can happen if we recorded a param change
10477 
10478           valis[k] = last_valueir + (next_valuesi[k] - last_valueir) / (tc_diff / TICKS_PER_SECOND_DBL) *
10479                      ((tc_diff2 = (tc - weed_get_int64_value((weed_plant_t *)lpc[j],
10480                                    WEED_LEAF_TIMECODE, NULL))) / TICKS_PER_SECOND_DBL) + .5;
10481           valis[k + 1] = last_valueig + (next_valuesi[k + 1] - last_valueig) / (tc_diff / TICKS_PER_SECOND_DBL) *
10482                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10483           valis[k + 2] = last_valueib + (next_valuesi[k + 2] - last_valueib) / (tc_diff / TICKS_PER_SECOND_DBL) *
10484                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10485           valis[k + 3] = last_valueia + (next_valuesi[k + 3] - last_valueia) / (tc_diff / TICKS_PER_SECOND_DBL) *
10486                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10487 
10488           lives_free(last_valuesi);
10489           lives_free(next_valuesi);
10490         } else {
10491           if (!lpc[j]) {
10492             // before first change
10493             valds[k] = get_default_element_double(param, j, 4, 0);
10494             valds[k + 1] = get_default_element_double(param, j, 4, 1);
10495             valds[k + 2] = get_default_element_double(param, j, 4, 2);
10496             valds[k + 3] = get_default_element_double(param, j, 4, 3);
10497             j += 4;
10498             continue;
10499           }
10500           if (!npc[j]) {
10501             // after last change
10502             xnum = weed_leaf_num_elements((weed_plant_t *)lpc[j], WEED_LEAF_VALUE);
10503             if (xnum > k) {
10504               nvalds = weed_get_double_array((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, NULL);
10505               valds[k] = nvalds[k];
10506               valds[k + 1] = nvalds[k + 1];
10507               valds[k + 2] = nvalds[k + 2];
10508               valds[k + 3] = nvalds[k + 3];
10509               lives_free(nvalds);
10510             } else {
10511               valds[k] = get_default_element_double(param, j, 4, 0);
10512               valds[k + 1] = get_default_element_double(param, j, 4, 1);
10513               valds[k + 2] = get_default_element_double(param, j, 4, 2);
10514               valds[k + 3] = get_default_element_double(param, j, 4, 3);
10515             }
10516             j += 4;
10517             continue;
10518           }
10519 
10520           next_valuesd = weed_get_double_array((weed_plant_t *)npc[j], WEED_LEAF_VALUE, NULL);
10521           last_valuesd = weed_get_double_array_counted((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, &xnum);
10522           if (xnum > k) {
10523             last_valuedr = last_valuesd[k];
10524             last_valuedg = last_valuesd[k + 1];
10525             last_valuedb = last_valuesd[k + 2];
10526             last_valueda = last_valuesd[k + 3];
10527           } else {
10528             last_valuedr = get_default_element_double(param, j, 4, 0);
10529             last_valuedg = get_default_element_double(param, j, 4, 1);
10530             last_valuedb = get_default_element_double(param, j, 4, 2);
10531             last_valueda = get_default_element_double(param, j, 4, 3);
10532           }
10533           valds[k] = last_valuedr + (next_valuesd[k] - last_valuedr) / (tc_diff / TICKS_PER_SECOND_DBL) *
10534                      ((tc_diff2 = (tc - weed_get_int64_value((weed_plant_t *)lpc[j],
10535                                    WEED_LEAF_TIMECODE, NULL))) / TICKS_PER_SECOND_DBL);
10536           valds[k + 1] = last_valuedg + (next_valuesd[k + 1] - last_valuedg) / (tc_diff / TICKS_PER_SECOND_DBL) *
10537                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10538           valds[k + 2] = last_valuedb + (next_valuesd[k + 2] - last_valuedb) / (tc_diff / TICKS_PER_SECOND_DBL) *
10539                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10540           valds[k + 3] = last_valueda + (next_valuesd[k + 3] - last_valuedb) / (tc_diff / TICKS_PER_SECOND_DBL) *
10541                          (tc_diff2 / TICKS_PER_SECOND_DBL) + .5;
10542 
10543           lives_free(last_valuesd);
10544           lives_free(next_valuesd);
10545         }
10546         j += 4;
10547         break;
10548       } // cspace
10549       break; // color
10550     case WEED_PARAM_INTEGER:
10551       if (weed_param_get_nchoices(param) > 0) {
10552         // no interpolation
10553         if (npc[j] && get_event_timecode((weed_plant_t *)npc[j]) == tc) {
10554           nvalis = weed_get_int_array((weed_plant_t *)npc[j], WEED_LEAF_VALUE, NULL);
10555           valis[j] = nvalis[j];
10556           lives_free(nvalis);
10557           continue;
10558         } else {
10559           // use last_pchange value
10560           xnum = weed_leaf_num_elements((weed_plant_t *)lpc[j], WEED_LEAF_VALUE);
10561           if (xnum > j) {
10562             nvalis = weed_get_int_array((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, NULL);
10563             valis[j] = nvalis[j];
10564             lives_free(nvalis);
10565           } else valis[j] = get_default_element_int(param, j, 1, 0);
10566           continue;
10567         }
10568       } else {
10569         if (!lpc[j]) {
10570           // before first change
10571           valis[j] = get_default_element_int(param, j, 1, 0);
10572           continue;
10573         }
10574         if (!npc[j]) {
10575           // after last change
10576           xnum = weed_leaf_num_elements((weed_plant_t *)lpc[j], WEED_LEAF_VALUE);
10577           if (xnum > j) {
10578             nvalis = weed_get_int_array((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, NULL);
10579             valis[j] = nvalis[j];
10580             lives_free(nvalis);
10581           } else valis[j] = get_default_element_int(param, j, 1, 0);
10582           continue;
10583         }
10584 
10585         next_valuesi = weed_get_int_array((weed_plant_t *)npc[j], WEED_LEAF_VALUE, NULL);
10586         last_valuesi = weed_get_int_array_counted((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, &xnum);
10587         if (xnum > j) last_valuei = last_valuesi[j];
10588         else last_valuei = get_default_element_int(param, j, 1, 0);
10589 
10590         valis[j] = last_valuei + (next_valuesi[j] - last_valuei) / (tc_diff / TICKS_PER_SECOND_DBL) *
10591                    ((tc - weed_get_int64_value((weed_plant_t *)lpc[j], WEED_LEAF_TIMECODE, NULL)) / TICKS_PER_SECOND_DBL) + .5;
10592         lives_free(last_valuesi);
10593         lives_free(next_valuesi);
10594         break;
10595       }
10596     case WEED_PARAM_SWITCH:
10597       // no interpolation
10598       if (npc[j] && get_event_timecode((weed_plant_t *)npc[j]) == tc) {
10599         nvalis = weed_get_boolean_array((weed_plant_t *)npc[j], WEED_LEAF_VALUE, NULL);
10600         valis[j] = nvalis[j];
10601         lives_free(nvalis);
10602         continue;
10603       } else {
10604         // use last_pchange value
10605         xnum = weed_leaf_num_elements((weed_plant_t *)lpc[j], WEED_LEAF_VALUE);
10606         if (xnum > j) {
10607           nvalis = weed_get_boolean_array((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, NULL);
10608           valis[j] = nvalis[j];
10609           lives_free(nvalis);
10610         } else valis[j] = get_default_element_bool(param, j);
10611         continue;
10612       }
10613       break;
10614     case WEED_PARAM_TEXT:
10615       // no interpolation
10616       valss = weed_get_string_array(param, WEED_LEAF_VALUE, NULL);
10617       if (npc[j] && get_event_timecode((weed_plant_t *)npc[j]) == tc) {
10618         nvalss = weed_get_string_array((weed_plant_t *)npc[j], WEED_LEAF_VALUE, NULL);
10619         valss[j] = lives_strdup(nvalss[j]);
10620         for (k = 0; k < num_values; k++) lives_free(nvalss[k]);
10621         lives_free(nvalss);
10622         weed_set_string_array(param, WEED_LEAF_VALUE, num_values, valss);
10623         for (k = 0; k < num_values; k++) lives_free(valss[k]);
10624         lives_free(valss);
10625         continue;
10626       } else {
10627         // use last_pchange value
10628         xnum = weed_leaf_num_elements((weed_plant_t *)lpc[j], WEED_LEAF_VALUE);
10629         if (xnum > j) {
10630           nvalss = weed_get_string_array((weed_plant_t *)lpc[j], WEED_LEAF_VALUE, NULL);
10631           valss[j] = lives_strdup(nvalss[j]);
10632           for (k = 0; k < xnum; k++) lives_free(nvalss[k]);
10633           lives_free(nvalss);
10634         } else valss[j] = get_default_element_string(param, j);
10635         weed_set_string_array(param, WEED_LEAF_VALUE, num_values, valss);
10636         for (k = 0; k < num_values; k++) lives_free(valss[k]);
10637         lives_free(valss);
10638         continue;
10639       }
10640       break;
10641     } // parameter ptype
10642   } // j
10643 
10644   switch (ptype) {
10645   case WEED_PARAM_FLOAT:
10646     weed_set_double_array(param, WEED_LEAF_VALUE, num_values, valds);
10647     lives_free(valds);
10648     break;
10649   case WEED_PARAM_COLOR:
10650     switch (cspace) {
10651     case WEED_COLORSPACE_RGB:
10652       if (weed_leaf_seed_type(wtmpl, WEED_LEAF_DEFAULT) == WEED_SEED_INT) {
10653         weed_set_int_array(param, WEED_LEAF_VALUE, num_values, valis);
10654         lives_free(valis);
10655       } else {
10656         weed_set_double_array(param, WEED_LEAF_VALUE, num_values, valds);
10657         lives_free(valds);
10658       }
10659       break;
10660     }
10661     break;
10662   case WEED_PARAM_INTEGER:
10663     weed_set_int_array(param, WEED_LEAF_VALUE, num_values, valis);
10664     lives_free(valis);
10665     break;
10666   case WEED_PARAM_SWITCH:
10667     weed_set_boolean_array(param, WEED_LEAF_VALUE, num_values, valis);
10668     lives_free(valis);
10669     break;
10670   }
10671 
10672   lives_free(npc);
10673   lives_free(lpc);
10674   return TRUE;
10675 }
10676 
10677 /**
10678    @brief
10679 
10680   interpolate all in_parameters for filter_instance inst, using void **pchain,
10681   which is an array of param_change events in temporal order
10682   values are calculated for timecode tc. We skip WEED_LEAF_HIDDEN parameters
10683 */
interpolate_params(weed_plant_t * inst,void ** pchains,weed_timecode_t tc)10684 boolean interpolate_params(weed_plant_t *inst, void **pchains, weed_timecode_t tc) {
10685   weed_plant_t **in_params;
10686   void *pchain;
10687   int num_params, offset = 0;
10688 
10689   do {
10690     if (!pchains  || (in_params = weed_instance_get_in_params(inst, &num_params)) == NULL) continue;
10691     for (int i = offset; i < offset + num_params; i++) {
10692       if (!is_hidden_param(inst, i - offset)) {
10693         pchain = pchains[i];
10694         if (pchain && WEED_PLANT_IS_EVENT((weed_plant_t *)pchain)
10695             && WEED_EVENT_IS_PARAM_CHANGE((weed_plant_t *)pchain)) {
10696           if (weed_param_value_irrelevant(in_params[i - offset])) continue;
10697           if (!interpolate_param(in_params[i - offset], pchain, tc)) {
10698             lives_free(in_params);
10699             return FALSE; // FALSE if param is not ready
10700 	    // *INDENT-OFF*
10701 	  }}}}
10702     // *INDENT-ON*
10703     offset += num_params;
10704     lives_free(in_params);
10705   } while ((inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, NULL)) != NULL);
10706 
10707   return TRUE;
10708 }
10709 
10710 
10711 ///////////////////////////////////////////////////////////
10712 ////// hashnames
10713 
10714 /**
10715    @brief
10716 
10717   return value should be freed after use
10718 
10719   make hashname from filter_idx
10720 
10721   if fullname is FALSE, return filename, filtername concatenated
10722   if fullname is TRUE, return filename, filtername, author, version concatenated
10723 
10724   if sep is not 0, we ignore the booleans and return filename, sep, filtername, sep, author concatenated
10725   (suitable to be fed into weed_filter_highest_version() later)
10726 
10727   use_extra_authors is only for backwards compatibility
10728 
10729 */
make_weed_hashname(int filter_idx,boolean fullname,boolean use_extra_authors,char sep,boolean subs)10730 char *make_weed_hashname(int filter_idx, boolean fullname, boolean use_extra_authors, char sep, boolean subs) {
10731   weed_plant_t *filter, *plugin_info;
10732 
10733   char plugin_fname[PATH_MAX];
10734   char *plugin_name, *filter_name, *filter_author, *filter_version, *hashname, *filename;
10735   char xsep[2];
10736   boolean use_micro = FALSE;
10737   int version;
10738   int type = 0;
10739 
10740   if (filter_idx < 0 || filter_idx >= num_weed_filters) return lives_strdup("");
10741 
10742   if (!fullname) type += 2;
10743   if (use_extra_authors) type++;
10744 
10745   if (sep != 0) {
10746     fullname = TRUE;
10747     use_extra_authors = FALSE;
10748     xsep[0] = sep;
10749     xsep[1] = 0;
10750   } else {
10751     xsep[0] = xsep[1] = 0;
10752     if (hashnames[filter_idx][type].string)
10753       return lives_strdup(hashnames[filter_idx][type].string);
10754   }
10755 
10756   filter = weed_filters[filter_idx];
10757 
10758   if (sep == 0 && hashnames[filter_idx][type].string) {
10759     return lives_strdup(hashnames[filter_idx][type].string);
10760   }
10761   if (weed_plant_has_leaf(filter, WEED_LEAF_PLUGIN_INFO)) {
10762     plugin_info = weed_get_plantptr_value(filter, WEED_LEAF_PLUGIN_INFO, NULL);
10763     if (weed_plant_has_leaf(plugin_info, WEED_LEAF_PACKAGE_NAME)) {
10764       filename = weed_get_string_value(plugin_info, WEED_LEAF_PACKAGE_NAME, NULL);
10765     } else {
10766       plugin_name = weed_get_string_value(plugin_info, WEED_LEAF_HOST_PLUGIN_NAME, NULL);
10767       lives_snprintf(plugin_fname, PATH_MAX, "%s", plugin_name);
10768       lives_free(plugin_name);
10769       get_filename(plugin_fname, TRUE);
10770       // should we really use utf-8 here ? (needs checking)
10771       filename = F2U8(plugin_fname);
10772     }
10773   } else {
10774     return lives_strdup("");
10775   }
10776   filter_name = weed_filter_get_name(filter);
10777 
10778   if (fullname) {
10779     if (!use_extra_authors || !weed_plant_has_leaf(filter, WEED_LEAF_EXTRA_AUTHORS))
10780       filter_author = weed_get_string_value(filter, WEED_LEAF_AUTHOR, NULL);
10781     else
10782       filter_author = weed_get_string_value(filter, WEED_LEAF_EXTRA_AUTHORS, NULL);
10783 
10784     version = weed_get_int_value(filter, WEED_LEAF_VERSION, NULL);
10785 
10786     if (*xsep) {
10787       hashname = lives_strconcat(filename, xsep, filter_name, xsep, filter_author, NULL);
10788     } else {
10789       if (use_micro) {
10790         int micro_version = weed_get_int_value(filter, WEED_LEAF_MICRO_VERSION, NULL);
10791         filter_version = lives_strdup_printf("%d.%d", version, micro_version);
10792       } else {
10793         filter_version = lives_strdup_printf("%d", version);
10794       }
10795       hashname = lives_strconcat(filename, filter_name, filter_author, filter_version, NULL);
10796       lives_free(filter_version);
10797     }
10798     lives_free(filter_author);
10799   } else {
10800     hashname = lives_strconcat(filename, filter_name, NULL);
10801   }
10802   lives_free(filter_name);
10803   lives_free(filename);
10804 
10805   if (subs) {
10806     char *xhashname = subst(hashname, " ", "_");
10807     if (lives_strcmp(xhashname, hashname)) {
10808       lives_free(hashname);
10809       return xhashname;
10810     }
10811     lives_free(hashname);
10812     lives_free(xhashname);
10813     return NULL;
10814   }
10815 
10816   //g_print("added filter %s\n",hashname);
10817   return hashname;
10818 }
10819 
10820 
fix_hashnames(const char * old)10821 static char *fix_hashnames(const char *old) {
10822   const char *alterations[4] = {"frei0rFrei0r: ", "lapdspaLADSPA: ", "libvisuallibvisual: ", NULL};
10823   const char *replacements[4] = {"Frei0r", "LADSPA", "libvisual", NULL};
10824   char *hashname_new = NULL;
10825   int i = 0;
10826   while (alterations[i]) {
10827     if (strstr(old, alterations[i])) {
10828       hashname_new = subst(old, alterations[i], replacements[i]);
10829       break;
10830     }
10831     i++;
10832   }
10833   if (hashname_new) {
10834     return hashname_new;
10835   }
10836   return lives_strdup(old);
10837 }
10838 
10839 
weed_get_idx_for_hashname(const char * hashname,boolean fullname)10840 int weed_get_idx_for_hashname(const char *hashname, boolean fullname) {
10841   int32_t numhash;
10842   char *xhashname = fix_hashnames(hashname);
10843   int type = 0;
10844   register int i;
10845 
10846   if (!fullname) type = 2;
10847 
10848   numhash = lives_string_hash(xhashname);
10849 
10850   for (i = 0; i < num_weed_filters; i++) {
10851     if (numhash == hashnames[i][type].hash) {
10852       if (!lives_utf8_strcasecmp(xhashname, hashnames[i][type].string)) {
10853         lives_free(xhashname);
10854         return i;
10855       }
10856     }
10857   }
10858 
10859   type += 4;
10860 
10861   if (hashnames && hashnames[0][type].string) {
10862     for (i = 0; i < num_weed_filters; i++) {
10863       if (numhash == hashnames[i][type].hash) {
10864         if (!lives_utf8_strcasecmp(xhashname, hashnames[i][type].string)) {
10865           lives_free(xhashname);
10866           return i;
10867 	  // *INDENT-OFF*
10868 	}}}}
10869   // *INDENT-ON*
10870 
10871   if (fullname) {
10872     type = 1;
10873     for (i = 0; i < num_weed_filters; i++) {
10874       if (numhash == hashnames[i][type].hash) {
10875         if (!lives_utf8_strcasecmp(xhashname, hashnames[i][type].string)) {
10876           lives_free(xhashname);
10877           return i;
10878 	  // *INDENT-OFF*
10879 	}}}}
10880   // *INDENT-ON*
10881 
10882   lives_free(xhashname);
10883   return -1;
10884 }
10885 
10886 
check_match(weed_plant_t * filter,const char * pkg,const char * fxname,const char * auth,int version)10887 static boolean check_match(weed_plant_t *filter, const char *pkg, const char *fxname, const char *auth, int version) {
10888   // perform template matching for a filter, see weed_get_indices_from_template()
10889   weed_plant_t *plugin_info;
10890 
10891   char plugin_fname[PATH_MAX];
10892   char *plugin_name, *filter_name, *filter_author;
10893 
10894   int filter_version;
10895 
10896   if (pkg && *pkg) {
10897     char *filename;
10898 
10899     if (weed_plant_has_leaf(filter, WEED_LEAF_PLUGIN_INFO)) {
10900       plugin_info = weed_get_plantptr_value(filter, WEED_LEAF_PLUGIN_INFO, NULL);
10901       if (weed_plant_has_leaf(plugin_info, WEED_LEAF_PACKAGE_NAME)) {
10902         filename = weed_get_string_value(plugin_info, WEED_LEAF_HOST_PLUGIN_NAME, NULL);
10903       } else {
10904         plugin_name = weed_get_string_value(plugin_info, WEED_LEAF_HOST_PLUGIN_NAME, NULL);
10905         lives_snprintf(plugin_fname, PATH_MAX, "%s", plugin_name);
10906         lives_freep((void **)&plugin_name);
10907         get_filename(plugin_fname, TRUE);
10908         filename = F2U8(plugin_fname);
10909       }
10910     } else {
10911       return FALSE;
10912     }
10913 
10914     if (lives_utf8_strcasecmp(pkg, filename)) {
10915       lives_freep((void **)&filename);
10916       return FALSE;
10917     }
10918     lives_freep((void **)&filename);
10919   }
10920 
10921   if (fxname && *fxname) {
10922     filter_name = weed_get_string_value(filter, WEED_LEAF_NAME, NULL);
10923     if (lives_utf8_strcasecmp(fxname, filter_name)) {
10924       lives_freep((void **)&filter_name);
10925       return FALSE;
10926     }
10927     lives_freep((void **)&filter_name);
10928   }
10929 
10930   if (auth && *auth) {
10931     filter_author = weed_get_string_value(filter, WEED_LEAF_AUTHOR, NULL);
10932     if (lives_utf8_strcasecmp(auth, filter_author)) {
10933       lives_freep((void **)&filter_author);
10934       return FALSE;
10935     }
10936     lives_freep((void **)&filter_author);
10937   }
10938 
10939   if (version > 0) {
10940     filter_version = weed_get_int_value(filter, WEED_LEAF_VERSION, NULL);
10941     if (version != filter_version) return FALSE;
10942   }
10943 
10944   return TRUE;
10945 }
10946 
10947 
10948 /**
10949    @brief
10950 
10951   generate a list of filter indices from a given template. Char values may be NULL or "" to signify match-any.
10952   version may be 0 to signify match-any
10953   otherwise values must match those specified
10954 
10955   returns: a list of int indices, with the last value == -1
10956   return value should be freed after use
10957 */
weed_get_indices_from_template(const char * pkg,const char * fxname,const char * auth,int version)10958 int *weed_get_indices_from_template(const char *pkg, const char *fxname, const char *auth, int version) {
10959   weed_plant_t *filter;
10960   int *rvals;
10961 
10962   int count = 1, count2 = 0;
10963 
10964   register int i;
10965 
10966   // count number of return values
10967   for (i = 0; i < num_weed_filters; i++) {
10968     filter = weed_filters[i];
10969 
10970     if (check_match(filter, pkg, fxname, auth, version)) {
10971       count++;
10972     }
10973   }
10974 
10975   // allocate storage space
10976   rvals = (int *)lives_malloc(count * sizint);
10977 
10978   // get return values
10979   for (i = 0; count2 < count - 1; i++) {
10980     filter = weed_filters[i];
10981 
10982     if (check_match(filter, pkg, fxname, auth, version)) {
10983       rvals[count2++] = i;
10984     }
10985   }
10986 
10987   // end-of-array indicator
10988   rvals[count2] = -1;
10989 
10990   return rvals;
10991 }
10992 
10993 
weed_filter_highest_version(const char * pkg,const char * fxname,const char * auth,int * xversion)10994 int weed_filter_highest_version(const char *pkg, const char *fxname, const char *auth, int *xversion) {
10995   int *allversions = weed_get_indices_from_template(pkg, fxname, auth, 0);
10996   int highestv = 0, version, i = 0, hidx = -1;
10997   char *hash;
10998   while ((version = allversions[i] != -1)) {
10999     if (version > highestv) {
11000       highestv = version;
11001     }
11002     i++;
11003   }
11004 
11005   lives_free(allversions);
11006   if (xversion) *xversion = highestv;
11007   hash = lives_strdup_printf("%s%s%s%d", pkg, fxname, auth, highestv);
11008   hidx =  weed_get_idx_for_hashname(hash, TRUE);
11009   lives_free(hash);
11010   return hidx;
11011 }
11012 
11013 
get_weed_filter(int idx)11014 weed_plant_t *get_weed_filter(int idx) {
11015   if (idx > -1 && idx < num_weed_filters) return weed_filters[idx];
11016   return NULL;
11017 }
11018 
11019 
11020 /**
11021    @brief serialise a leaf
11022 
11023    serialise a leaf with key "key" to memory or to file
11024    for file, set fd >= 0 and mem to NULL
11025    for memory, pass the address of a memory area (which must be large enough to accept the data)
11026    if write_all is set then we first write the key name
11027 
11028    serialisation format is 4 bytes "size" (little-endian for file) followed by the data
11029    - strings are not NULL terminated
11030    - pointer types are converted to uint64_t before writing
11031 
11032    returns bytesize of serialised leaf
11033 
11034    format is [key_len (4 bytes) | key (key_len bytes)] seed_type (4 bytes) n_elements (4 bytes)
11035    then for each element: value_size (4 bytes) value
11036 */
weed_leaf_serialise(int fd,weed_plant_t * plant,const char * key,boolean write_all,unsigned char ** mem)11037 static size_t weed_leaf_serialise(int fd, weed_plant_t *plant, const char *key, boolean write_all, unsigned char **mem) {
11038   void *value = NULL, *valuer = NULL;
11039 
11040   size_t totsize = 0;
11041 
11042   weed_size_t vlen;
11043   weed_size_t keylen = (weed_size_t)lives_strlen(key);
11044 
11045   int st, ne;
11046   int j;
11047 
11048   // write errors will be checked for by the calling function
11049 
11050   if (write_all) {
11051     // write byte length of key, followed by key in utf-8
11052     if (!mem) {
11053       lives_write_le_buffered(fd, &keylen, 4, TRUE);
11054       lives_write_buffered(fd, key, (size_t)keylen, TRUE);
11055     } else {
11056       lives_memcpy(*mem, &keylen, 4);
11057       *mem += 4;
11058       lives_memcpy(*mem, key, (size_t)keylen);
11059       *mem += keylen;
11060     }
11061     totsize += 4 + keylen;
11062   }
11063 
11064   // write seed type and number of elements
11065   st = weed_leaf_seed_type(plant, key);
11066   if (!mem && st == WEED_SEED_PLANTPTR) st = WEED_SEED_VOIDPTR;
11067 
11068   if (!mem) lives_write_le_buffered(fd, &st, 4, TRUE);
11069   else {
11070     lives_memcpy(*mem, &st, 4);
11071     *mem += 4;
11072   }
11073   ne = weed_leaf_num_elements(plant, key);
11074   if (!mem) lives_write_le_buffered(fd, &ne, 4, TRUE);
11075   else {
11076     lives_memcpy(*mem, &ne, 4);
11077     *mem += 4;
11078   }
11079 
11080   totsize += 8;
11081 
11082   // for pixel_data we do special handling
11083   // older LiVES versions wrote the bytesize (4 bytes, then the data
11084   if (!mem && !lives_strcmp(key, WEED_LEAF_PIXEL_DATA)) {
11085     weed_layer_t *layer = (weed_layer_t *)plant;
11086     int nplanes;
11087     int *rowstrides = weed_layer_get_rowstrides(layer, &nplanes);
11088     int pal = weed_layer_get_palette(layer);
11089     int width = weed_layer_get_width(layer);
11090     int height = weed_layer_get_height(layer);
11091     int ival = 0;
11092     boolean contig = FALSE;
11093     size_t padding = 0;
11094     size_t pdsize = 0;
11095 
11096     uint8_t **pixel_data = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11097     /// new style: we will write 4 bytes 0, then possibly further padding bytes,
11098     /// following this, a 4 byte identifier: 0x57454544, and 4 bytes version *little endian. The current version is 0x01000000
11099     /// this is then followed by 4 bytes nplanes, 4 bytes palette, 4 bytes width, 4 bytes height.
11100     /// width is in macropixel size - for UYVY and YUYV each macropixel is 4 bytes and maps to 2 screen pixels
11101     /// then for each plane: rowstride 4 bytes, data size 8 bytes (the data size is plane height * rowstride + padding)
11102     /// finally the pixel data
11103     lives_write_le_buffered(fd, &ival, 4, TRUE);
11104     ival = 0x44454557;
11105     lives_write_le_buffered(fd, &ival, 4, TRUE);
11106     ival = 1; /// version
11107     lives_write_le_buffered(fd, &ival, 4, TRUE);
11108     lives_write_le_buffered(fd, &nplanes, 4, TRUE);
11109     lives_write_le_buffered(fd, &pal, 4, TRUE);
11110     lives_write_le_buffered(fd, &width, 4, TRUE);
11111     lives_write_le_buffered(fd, &height, 4, TRUE);
11112 
11113     totsize += 28;
11114 
11115     if (weed_get_boolean_value(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, NULL) == WEED_TRUE) {
11116       contig = TRUE;
11117     }
11118     padding = 0;
11119     for (j = 0; j < nplanes; j++) {
11120       vlen = (weed_size_t)((double)height * weed_palette_get_plane_ratio_vertical(pal, j) * (double)rowstrides[j]);
11121       if (contig && j < nplanes - 1) padding = pixel_data[j + 1] - pixel_data[j] - vlen;
11122       vlen += padding;
11123       lives_write_le_buffered(fd, &rowstrides[j], 4, TRUE);
11124       lives_write_le_buffered(fd, &vlen, 8, TRUE);
11125       pdsize += vlen;
11126       totsize += 12;
11127     }
11128     if (!contig) {
11129       for (j = 0; j < nplanes; j++) {
11130         vlen = (weed_size_t)((double)height * weed_palette_get_plane_ratio_vertical(pal, j) * (double)rowstrides[j]);
11131         lives_write_buffered(fd, (const char *)pixel_data[j], vlen, TRUE);
11132         totsize += vlen;
11133       }
11134     } else {
11135       lives_write_buffered(fd, (const char *)pixel_data[0], pdsize, TRUE);
11136       totsize += pdsize;
11137     }
11138     lives_free(rowstrides);
11139     lives_free(pixel_data);
11140   } else {
11141     // for each element, write the data size followed by the data
11142     for (j = 0; j < ne; j++) {
11143       vlen = (weed_size_t)weed_leaf_element_size(plant, key, j);
11144       if (!mem && vlen == 0) {
11145         // we need to do this because NULL pointers return a size of 0, and older versions of LiVES
11146         // expected to always read 8 bytes for a void *
11147         if (st > 64) vlen = 8;
11148       }
11149       if (st != WEED_SEED_STRING) {
11150         if (vlen == 0) value = NULL;
11151         else {
11152           value = lives_malloc((size_t)vlen);
11153           weed_leaf_get(plant, key, j, value);
11154         }
11155       } else {
11156         // need to create a buffer to receive the string + terminating NULL
11157         value = lives_malloc((size_t)(vlen + 1));
11158         // weed_leaf_get() will assume it's a pointer to a variable of the correct type, and fill in the value
11159         weed_leaf_get(plant, key, j, &value);
11160       }
11161 
11162       if (!mem && weed_leaf_seed_type(plant, key) > 64) {
11163         // save voidptr as 64 bit
11164         valuer = (uint64_t *)lives_malloc(sizeof(uint64_t));
11165 
11166         // 'valuer' is a void * (size 8). and 'value' is void * (of whatever size)
11167         // but we can cast 'value' to a (void **),
11168         // and then we can dereference it and cast that value to a uint64, and store the result in valuer
11169         *((uint64_t *)valuer) = (uint64_t)(*((void **)value));
11170         vlen = sizeof(uint64_t);
11171       } else valuer = value;
11172 
11173       if (!mem) {
11174         lives_write_le_buffered(fd, &vlen, 4, TRUE);
11175         if (st != WEED_SEED_STRING) {
11176           lives_write_le_buffered(fd, valuer, (size_t)vlen, TRUE);
11177         } else lives_write_buffered(fd, (const char *)valuer, (size_t)vlen, TRUE);
11178       } else {
11179         lives_memcpy(*mem, &vlen, 4);
11180         *mem += 4;
11181         if (vlen > 0) {
11182           lives_memcpy(*mem, value, (size_t)vlen);
11183           *mem += vlen;
11184         }
11185       }
11186       if (valuer != value) lives_freep((void **)&valuer);
11187       totsize += 4 + vlen;
11188       lives_freep((void **)&value);
11189     }
11190   }
11191 
11192   // write errors should be checked for by the calling function
11193 
11194   return totsize;
11195 }
11196 
11197 
weed_plant_serialise(int fd,weed_plant_t * plant,unsigned char ** mem)11198 size_t weed_plant_serialise(int fd, weed_plant_t *plant, unsigned char **mem) {
11199   // serialise an entire plant
11200   //
11201   // write errors should be checked for by the calling function
11202 
11203   // returns the bytesize of the serialised plant
11204 
11205   size_t totsize = 0;
11206   weed_size_t nleaves;
11207   char **proplist = weed_plant_list_leaves(plant, &nleaves);
11208   char *prop;
11209   int i = (int)nleaves;
11210   int pd_needed = 0, pd_reqs = 0;
11211 
11212   if (WEED_IS_LAYER(plant)) pd_needed = 1;
11213 
11214   if (!mem) lives_write_le_buffered(fd, &i, 4, TRUE); // write number of leaves
11215   else {
11216     lives_memcpy(*mem, &i, 4);
11217     *mem += 4;
11218   }
11219 
11220   totsize += 4;
11221 
11222   // serialise the "type" leaf first, so that we know this is a new plant when deserialising
11223   totsize += weed_leaf_serialise(fd, plant, WEED_LEAF_TYPE, TRUE, mem);
11224   lives_free(proplist[0]);
11225 
11226   for (i = 1; (prop = proplist[i]); i++) {
11227     // write each leaf and key
11228     if (pd_needed > 0) {
11229       // write pal, height, rowstrides before pixel_data.
11230       if (!lives_strcmp(prop, WEED_LEAF_PIXEL_DATA)) {
11231         if (pd_reqs > 3) pd_needed = 0;
11232         else {
11233           ++pd_needed;
11234           lives_free(prop);
11235           continue;
11236         }
11237       } else {
11238         if (pd_reqs < 4 && (!strcmp(prop, WEED_LEAF_WIDTH) || !strcmp(prop, WEED_LEAF_HEIGHT)
11239                             || !lives_strcmp(prop, WEED_LEAF_CURRENT_PALETTE)
11240                             || !strcmp(prop, WEED_LEAF_ROWSTRIDES))) {
11241           if (++pd_reqs == 4 && pd_needed > 1) {
11242             totsize += weed_leaf_serialise(fd, plant, prop, TRUE, mem);
11243             lives_free(prop);
11244             totsize += weed_leaf_serialise(fd, plant, WEED_LEAF_PIXEL_DATA, TRUE, mem);
11245             pd_needed  = 0;
11246             continue;
11247 	    // *INDENT-OFF*
11248 	  }}}}
11249     // *INDENT-ON*
11250 
11251     totsize += weed_leaf_serialise(fd, plant, prop, TRUE, mem);
11252     lives_free(prop);
11253   }
11254   lives_freep((void **)&proplist);
11255   return totsize;
11256 }
11257 
11258 
weed_plant_mutate(weed_plantptr_t plant,int32_t newtype)11259 static int32_t weed_plant_mutate(weed_plantptr_t plant, int32_t newtype) {
11260   // beware of mutant plants....
11261   int32_t flags = weed_leaf_get_flags(plant, WEED_LEAF_TYPE);
11262   // clear the default flags to allow the "type" leaf to be altered
11263   weed_leaf_set_flags(plant, WEED_LEAF_TYPE, flags & ~(WEED_FLAG_IMMUTABLE));
11264   weed_set_int_value(plant, WEED_LEAF_TYPE, newtype);
11265   // lock the "type" leaf again so it cannot be altered accidentally
11266   weed_leaf_set_flags(plant, WEED_LEAF_TYPE, WEED_FLAG_IMMUTABLE);
11267   return weed_plant_get_type(plant);
11268 }
11269 
11270 
11271 #define REALIGN_MAX (40 * 1024 * 1024)  /// 40 MiB "should be enough for anyone"
11272 #define MAX_FRAME_SIZE MILLIONS(100)
11273 #define MAX_FRAME_SIZE64 3019898880
11274 
realign_typeleaf(int fd,weed_plant_t * plant)11275 static int realign_typeleaf(int fd, weed_plant_t *plant) {
11276   uint8_t buff[12];
11277   const char XMATCH[8] = {4, 0, 0, 0, 't', 'y', 'p', 'e'};
11278   int type, nl;
11279   register uint64_t count = REALIGN_MAX;
11280   register int len;
11281 
11282   if (lives_read_buffered(fd, buff, 12, TRUE) < 12) return 0;
11283   for (len = 8; len > 0; len--) {
11284     if (!memcmp((void *)(buff + 12 - len), (void *)(XMATCH + 8 - len), len)) break;
11285   }
11286   if (len == 8) len--;
11287 
11288   while (--count != 0) {
11289     if (buff[11] == XMATCH[len]) {
11290       len++;
11291       if (len == 8) {
11292         nl = (uint32_t)((buff[3] << 24) + (buff[2] << 16) + (buff[1] << 8) + buff[0]);
11293         // skip st, ne, valsize
11294         if (lives_read_buffered(fd, buff, 12, TRUE) < 12) return 0;
11295         if (lives_read_le_buffered(fd, &type, 4, TRUE) < 4) return 0;
11296         weed_plant_mutate(plant, type);
11297         return nl;
11298       }
11299     } else {
11300       if (len > 0) {
11301         len = 0;
11302         continue;
11303       }
11304     }
11305     lives_memmove(buff + 7 - len, buff + 8 - len, len + 4);
11306     if (lives_read_buffered(fd, &buff[11], 1, TRUE) < 1) return 0;
11307   }
11308   return 0;
11309 }
11310 
11311 /**
11312    @brief deserialise a weed leaf
11313    returns "type" on succes or a -ve error code on failure
11314 
11315    WEED_LEAF_HOST_DEFAULT and WEED_LEAF_TYPE sets key; otherwise we leave it as NULL to get the next
11316 
11317    if the input 'type' is 0 (WEED_PLANT_UNKNOWN) then it is set to 'type', unless an error occurs
11318 
11319    if check_key set to TRUE - check that we read the correct key seed_type and n_elements
11320 
11321    return values:
11322     -1 : check_key key mismatch
11323     -2 : "type" leaf was not an INT
11324     -3 : "type" leaf has invalid element count
11325     -4 : short read
11326     -5 : memory allocation error
11327     -6 : unknown seed_type
11328     -7 : plant "type" mismatch
11329     -8 : 'type' was < 0
11330     -9 : key length mismatch
11331    - 10 : key length too long
11332    - 11 : data length too long
11333     -12 : data length too short
11334 */
weed_leaf_deserialise(int fd,weed_plant_t * plant,const char * key,unsigned char ** mem,boolean check_key)11335 static int weed_leaf_deserialise(int fd, weed_plant_t *plant, const char *key, unsigned char **mem,
11336                                  boolean check_key) {
11337   void **values = NULL;
11338 
11339   ssize_t bytes;
11340 
11341   int32_t *ints;
11342   double *dubs;
11343   int64_t *int64s;
11344 
11345   weed_size_t len;
11346   weed_size_t vlen;
11347   //weed_size_t vlen64;
11348   weed_size_t vlen64_tot = 0;
11349 
11350   char *mykey = NULL;
11351   char *msg;
11352 
11353   boolean check_ptrs = FALSE;
11354   int32_t st; // seed type
11355   int32_t ne; // num elems
11356   int32_t type = 0;
11357   int error;
11358 
11359   int i, j;
11360 
11361   if (!key && check_key) {
11362     check_ptrs = TRUE;
11363     check_key = FALSE;
11364   }
11365 
11366   if (!key || check_key) {
11367     // key length
11368     if (!mem) {
11369       if (lives_read_le_buffered(fd, &len, 4, TRUE) < 4) {
11370         return -4;
11371       }
11372     } else {
11373       lives_memcpy(&len, *mem, 4);
11374       *mem += 4;
11375     }
11376 
11377     if (check_key && len != lives_strlen(key)) {
11378       if (prefs->show_dev_opts)
11379         g_print("len for %s was %d\n", key, len);
11380       return -9;
11381     }
11382     if (len > MAX_WEED_STRLEN) return -10;
11383 
11384     mykey = (char *)lives_malloc((size_t)len + 1);
11385     if (!mykey) return -5;
11386 
11387     // read key
11388     if (!mem) {
11389       if (lives_read_buffered(fd, mykey, (size_t)len, TRUE) < len) {
11390         type = -4;
11391         goto done;
11392       }
11393     } else {
11394       lives_memcpy(mykey, *mem, (size_t)len);
11395       *mem += len;
11396     }
11397     lives_memset(mykey + (size_t)len, 0, 1);
11398     //g_print("got key %s\n", mykey);
11399     if (check_key && lives_strcmp(mykey, key)) {
11400       type = -1;
11401       goto done;
11402     }
11403 
11404     if (!key) key = mykey;
11405     else {
11406       lives_freep((void **)&mykey);
11407     }
11408   }
11409 
11410   if (!mem) {
11411     if (lives_read_le_buffered(fd, &st, 4, TRUE) < 4) {
11412       type = -4;
11413       goto done;
11414     }
11415   } else {
11416     lives_memcpy(&st, *mem, 4);
11417     *mem += 4;
11418   }
11419   if (st < 64 && (st != WEED_SEED_INT && st != WEED_SEED_BOOLEAN && st != WEED_SEED_DOUBLE && st != WEED_SEED_INT64 &&
11420                   st != WEED_SEED_STRING && st != WEED_SEED_VOIDPTR && st != WEED_SEED_PLANTPTR)) {
11421     if (prefs->show_dev_opts) {
11422       g_printerr("unknown seed type %d %s\n", st, mykey);
11423       break_me("ink. seed type in w.l. deser");
11424     }
11425     type = -6;
11426     goto done;
11427   }
11428 
11429   if (check_key && !strcmp(key, WEED_LEAF_TYPE)) {
11430     // for the WEED_LEAF_TYPE leaf perform some extra checks
11431     if (st != WEED_SEED_INT) {
11432       type = -2;
11433       goto done;
11434     }
11435   }
11436 
11437   if (!mem) {
11438     if (lives_read_le_buffered(fd, &ne, 4, TRUE) < 4) {
11439       type = -4;
11440       goto done;
11441     }
11442   } else {
11443     lives_memcpy(&ne, *mem, 4);
11444     *mem += 4;
11445   }
11446   if (ne > MAX_WEED_ELEMENTS) {
11447     type = -11;
11448     goto done;
11449   }
11450 
11451   if (!lives_strcmp(key, WEED_LEAF_PIXEL_DATA)) {
11452     //g_print("ne was %d\n", ne);
11453     if (ne > 4) {
11454       // max planes is 4 (YUVA4444P)
11455       for (j = ne ; j >= 0; lives_freep((void **)&values[j--]));
11456       values = NULL;
11457       type = -11;
11458       goto done;
11459     }
11460   }
11461   if (ne > 0) {
11462     values = (void **)lives_malloc(ne * sizeof(void *));
11463     if (!values) {
11464       type = -5;
11465       goto done;
11466     }
11467   } else values = NULL;
11468 
11469   if (check_key && !strcmp(key, WEED_LEAF_TYPE)) {
11470     // for the WEED_LEAF_TYPE leaf perform some extra checks
11471     if (ne != 1) {
11472       type = -3;
11473       goto done;
11474     }
11475   }
11476 
11477   // for pixel_data we do special handling
11478   if (!mem && !lives_strcmp(key, WEED_LEAF_PIXEL_DATA)) {
11479     int width, height, pal = 0, *rs = NULL, nplanes = ne;
11480     height = weed_layer_get_height(plant);
11481     if (height > 0) {
11482       width = weed_layer_get_width(plant);
11483       if (width > 0) {
11484         pal = weed_layer_get_palette(plant);
11485         if (pal > 0) {
11486           rs = weed_layer_get_rowstrides(plant, &nplanes); {
11487             if (nplanes != ne) {
11488               LIVES_WARN("Invalid planes in retrieved layer");
11489 	      // *INDENT-OFF*
11490 	    }}}}}
11491     // *INDENT-ON*
11492 
11493     for (j = 0; j < ne; j++) {
11494       bytes = lives_read_le_buffered(fd, &vlen, 4, TRUE);
11495       if (bytes < 4) {
11496         for (--j; j >= 0; lives_freep((void **)&values[j--]));
11497         values = NULL;
11498         type = -4;
11499         goto done;
11500       }
11501 
11502       if (j == 0 && vlen == 0) {
11503         int id;
11504         bytes = lives_read_le_buffered(fd, &id, 4, TRUE);
11505         if (id == 0x44454557) {
11506           weed_layer_t *layer = (weed_layer_t *)plant;
11507           int ver;
11508           uint64_t *vlen64 = lives_calloc(nplanes, 8);
11509           //size_t vlen64_tot = 0;
11510           int *rs = lives_calloc(nplanes, sizint);
11511 
11512           bytes = lives_read_le_buffered(fd, &ver, 4, TRUE);
11513           bytes = lives_read_le_buffered(fd, &nplanes, 4, TRUE);
11514           bytes = lives_read_le_buffered(fd, &pal, 4, TRUE);
11515           weed_layer_set_palette(layer, pal);
11516           bytes = lives_read_le_buffered(fd, &width, 4, TRUE);
11517           bytes = lives_read_le_buffered(fd, &height, 4, TRUE);
11518           weed_layer_set_size(layer, width, height);
11519           vlen64 = lives_calloc(nplanes, 8);
11520           rs = lives_calloc(nplanes, 4);
11521           for (int p = 0; p < nplanes; p++) {
11522             bytes = lives_read_le_buffered(fd, &rs[p], 4, TRUE);
11523             bytes = lives_read_le_buffered(fd, &vlen64[p], 8, TRUE);
11524             vlen64_tot += vlen64[p];
11525           }
11526 
11527           if (vlen64_tot > MAX_FRAME_SIZE64) {
11528             values = NULL;
11529             type = -11;
11530             lives_free(rs);
11531             lives_free(vlen64);
11532             goto done;
11533           }
11534 
11535           weed_layer_set_rowstrides(layer, rs, nplanes);
11536 
11537           lives_free(rs);
11538 
11539           values[0] = lives_calloc(ALIGN_CEIL(vlen64_tot + EXTRA_BYTES, 16) / 16, 16);
11540 
11541           if (!values[0]) {
11542             msg = lives_strdup_printf("Could not allocate %d bytes for deserialised frame", vlen);
11543             LIVES_ERROR(msg);
11544             lives_free(msg);
11545             lives_free(vlen64);
11546             weed_set_voidptr_value(plant, WEED_LEAF_PIXEL_DATA, NULL);
11547             type = -5;
11548             goto done;
11549           }
11550 
11551           if (lives_read_buffered(fd, values[0], vlen64_tot, TRUE) != vlen64_tot) {
11552             ///// do something
11553             lives_free(values[0]);
11554             lives_free(values);
11555             values = NULL;
11556             lives_free(vlen64);
11557             type = -4;
11558             goto done;
11559           }
11560           for (i = 1; i < nplanes; i++) {
11561             values[i] = values[i - 1] + vlen64[i];
11562           }
11563           if (nplanes > 1)
11564             weed_set_boolean_value(plant, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, WEED_TRUE);
11565           lives_free(vlen64);
11566         } else {
11567           /// size 0, bad ID
11568           values = NULL;
11569           type = -12;
11570           goto done;
11571         }
11572       } else {
11573         //g_print("vlen was %d\n", vlen);
11574         if (vlen > MAX_FRAME_SIZE) {
11575           for (--j; j >= 0; lives_freep((void **)&values[j--]));
11576           values = NULL;
11577           type = -11;
11578           goto done;
11579         }
11580         if (rs && vlen != rs[j] * height * weed_palette_get_plane_ratio_vertical(pal, j)) {
11581           int xrs, xw, xh, psize = pixel_size(pal);
11582           xrs = vlen / height;
11583           xw = xrs / psize;
11584           xh = vlen / xrs;
11585 
11586           LIVES_WARN("invalid frame size recovering layer");
11587           msg = lives_strdup_printf(" for plane %d, size is %d. Should be %d. Will adjust rs to %d, width to %d "
11588                                     "and height to %d\n", j, vlen, rs[j] * height, xrs, xw, xh);
11589           LIVES_WARN(msg);
11590           lives_free(msg);
11591           weed_layer_set_width(plant, (float)xw / weed_palette_get_plane_ratio_horizontal(pal, j));
11592           weed_layer_set_height(plant, (float)xh / weed_palette_get_plane_ratio_vertical(pal, j));
11593           rs[j] = xrs;
11594           weed_layer_set_rowstrides(plant, rs, nplanes);
11595         }
11596 
11597         values[j] = lives_calloc(ALIGN_CEIL(vlen + EXTRA_BYTES, 16) / 16, 16);
11598         if (!values[j]) {
11599           msg = lives_strdup_printf("Could not allocate %d bytes for deserialised frame", vlen);
11600           LIVES_ERROR(msg);
11601           lives_free(msg);
11602           for (--j; j >= 0; j--) lives_free(values[j]);
11603           weed_set_voidptr_value(plant, WEED_LEAF_PIXEL_DATA, NULL);
11604           type = -5;
11605           goto done;
11606         }
11607         if (lives_read_buffered(fd, values[j], vlen, TRUE) != vlen) {
11608 
11609 
11610         }
11611         if (j >= nplanes) lives_free(values[j]);
11612       }
11613     }
11614     if (plant) {
11615       weed_set_voidptr_array(plant, WEED_LEAF_PIXEL_DATA, nplanes, values);
11616       while (nplanes != 0) values[--nplanes] = NULL; /// prevent "values" from being freed because we copy-by-value
11617     }
11618     goto done;
11619   } else {
11620     for (i = 0; i < ne; i++) {
11621       if (!mem) {
11622         bytes = lives_read_le_buffered(fd, &vlen, 4, TRUE);
11623         if (bytes < 4) {
11624           for (--i; i >= 0; lives_freep((void **)&values[i--]));
11625           values = NULL;
11626           type = -4;
11627           goto done;
11628         }
11629       } else {
11630         lives_memcpy(&vlen, *mem, 4);
11631         *mem += 4;
11632       }
11633 
11634       if (st == WEED_SEED_STRING) {
11635         if (vlen > MAX_WEED_STRLEN) {
11636           for (--i; i >= 0; lives_freep((void **)&values[i--]));
11637           values = NULL;
11638           type = -11;
11639           goto done;
11640         }
11641         values[i] = lives_malloc((size_t)vlen + 1);
11642       } else {
11643         if (vlen == 0) {
11644           if (st >= 64) {
11645             values[i] = NULL;
11646           } else {
11647             values = NULL;
11648             type = -4;
11649             goto done;
11650           }
11651         } else values[i] = lives_malloc((size_t)vlen);
11652       }
11653       if (vlen > 0) {
11654         if (!mem) {
11655           if (st != WEED_SEED_STRING)
11656             bytes = lives_read_le_buffered(fd, values[i], vlen, TRUE);
11657           else
11658             bytes = lives_read_buffered(fd, values[i], vlen, TRUE);
11659           if (bytes < vlen) {
11660             for (--i; i >= 0; lives_freep((void **)&values[i--]));
11661             values = NULL;
11662             type = -4;
11663             goto done;
11664           }
11665         } else {
11666           lives_memcpy(values[i], *mem, vlen);
11667           *mem += vlen;
11668         }
11669       }
11670       if (st == WEED_SEED_STRING) {
11671         lives_memset((char *)values[i] + vlen, 0, 1);
11672       }
11673     }
11674   }
11675 
11676   if (!strcmp(key, WEED_LEAF_TYPE)) {
11677     type = *(int32_t *)(values[0]);
11678     if (type < 0) return -8;
11679   }
11680   if (plant) {
11681     if (!values) {
11682       weed_leaf_set(plant, key, st, 0, NULL);
11683     } else {
11684       switch (st) {
11685       case WEED_SEED_INT:
11686       // fallthrough
11687       case WEED_SEED_BOOLEAN:
11688         ints = (int32_t *)lives_malloc(ne * 4);
11689         for (j = 0; j < ne; j++) ints[j] = *(int32_t *)values[j];
11690         if (!strcmp(key, WEED_LEAF_TYPE)) {
11691           if (weed_plant_has_leaf(plant, WEED_LEAF_TYPE)) {
11692             type = weed_get_int_value(plant, WEED_LEAF_TYPE, &error);
11693             if (type == WEED_PLANT_UNKNOWN) {
11694               type = weed_plant_mutate(plant, *ints);
11695             } else {
11696               if (*ints != type) {
11697                 msg = lives_strdup_printf("Type mismatch in deserialization: expected %d, got %d\n",
11698                                           type, *ints);
11699                 lives_free(ints);
11700                 LIVES_ERROR(msg);
11701                 lives_free(msg);
11702                 type = -7;
11703                 goto done;
11704               }
11705               // type already OK
11706             }
11707           } else {
11708             weed_leaf_set(plant, key, st, ne, (void *)ints);
11709           }
11710         } else {
11711           weed_leaf_set(plant, key, st, ne, (void *)ints);
11712         }
11713         lives_freep((void **)&ints);
11714         break;
11715       case WEED_SEED_DOUBLE:
11716         dubs = (double *)lives_malloc(ne * sizdbl);
11717         for (j = 0; j < ne; j++) dubs[j] = *(double *)values[j];
11718         weed_leaf_set(plant, key, st, ne, (void *)dubs);
11719         lives_freep((void **)&dubs);
11720         break;
11721       case WEED_SEED_INT64:
11722         int64s = (int64_t *)lives_malloc(ne * 8);
11723         for (j = 0; j < ne; j++) int64s[j] = *(int64_t *)values[j];
11724         weed_leaf_set(plant, key, st, ne, (void *)int64s);
11725         lives_freep((void **)&int64s);
11726         break;
11727       case WEED_SEED_STRING:
11728         weed_leaf_set(plant, key, st, ne, (void *)values);
11729         break;
11730       default:
11731         if (plant) {
11732           boolean add_leaf = TRUE;
11733           if (check_ptrs) {
11734             switch (weed_plant_get_type(plant)) {
11735             case WEED_PLANT_LAYER:
11736               if (lives_strcmp(key, WEED_LEAF_PIXEL_DATA)) {
11737                 add_leaf = FALSE;
11738               }
11739               break;
11740             default: break;
11741             }
11742           }
11743           if (add_leaf) {
11744             if (!mem && prefs->force64bit) {
11745               // force pointers to uint64_t
11746               uint64_t *voids = (uint64_t *)lives_malloc(ne * sizeof(uint64_t));
11747               for (j = 0; j < ne; j++) voids[j] = (uint64_t)(*(void **)values[j]);
11748               weed_leaf_set(plant, key, WEED_SEED_INT64, ne, (void *)voids);
11749               lives_freep((void **)&voids);
11750             } else {
11751               void **voids = (void **)lives_malloc(ne * sizeof(void *));
11752               for (j = 0; j < ne; j++) voids[j] = values[j];
11753               weed_leaf_set(plant, key, st, ne, (void *)voids);
11754               lives_freep((void **)&voids);
11755 	      // *INDENT-OFF*
11756 	    }}}}}}
11757   // *INDENT-ON*
11758 
11759 done:
11760 
11761   if (values) {
11762     for (i = 0; i < ne; i++) lives_freep((void **)&values[i]);
11763     lives_freep((void **)&values);
11764   }
11765   lives_freep((void **)&mykey);
11766   return type;
11767 }
11768 
11769 
weed_plant_deserialise(int fd,unsigned char ** mem,weed_plant_t * plant)11770 weed_plant_t *weed_plant_deserialise(int fd, unsigned char **mem, weed_plant_t *plant) {
11771   // if plant is NULL we create a new one
11772   // deserialise a plant from file fd or mem
11773   int numleaves;
11774   ssize_t bytes;
11775   int err;
11776   int32_t type = WEED_PLANT_UNKNOWN;
11777   boolean newd = FALSE, retried = FALSE;
11778   boolean block_unk_ptrs = FALSE;
11779   int bugfd = -1;
11780 
11781 realign:
11782 
11783   // caller should clear and check THREADVAR(read_failed)
11784   if (!mem) {
11785     if (fd == bugfd) {
11786       if (!plant) {
11787         // create a new plant with type unknown
11788         plant = weed_plant_new(WEED_PLANT_UNKNOWN);
11789         newd = TRUE;
11790       }
11791       numleaves = realign_typeleaf(fd, plant);
11792       if (numleaves == 0 || (type = weed_plant_get_type(plant)) <= 0) {
11793         if (newd) weed_plant_free(plant);
11794         return NULL;
11795       }
11796       bugfd = -1;
11797     } else {
11798       if ((bytes = lives_read_le_buffered(fd, &numleaves, 4, TRUE)) < 4) {
11799         bugfd = -1;
11800         return NULL;
11801       }
11802     }
11803   } else {
11804     lives_memcpy(&numleaves, *mem, 4);
11805     *mem += 4;
11806   }
11807 
11808   if (!plant) {
11809     // create a new plant with type unknown
11810     plant = weed_plant_new(type);
11811     newd = TRUE;
11812   }
11813 
11814   if (type == WEED_PLANT_UNKNOWN) {
11815     if ((type = weed_leaf_deserialise(fd, plant, WEED_LEAF_TYPE, mem, TRUE)) <= 0) {
11816       // check the WEED_LEAF_TYPE leaf first
11817       if (type != -5) {  // all except malloc error
11818         char *badfname = filename_from_fd(NULL, fd);
11819         char *msg = lives_strdup_printf("Data mismatch (%d) reading from\n%s", type, badfname ? badfname : "unknown file");
11820         LIVES_ERROR(msg);
11821         lives_free(msg);
11822         bugfd = fd;
11823       }
11824       if (newd) {
11825         weed_plant_free(plant);
11826         plant = NULL;
11827       }
11828       if (bugfd == fd && !retried) {
11829         retried = TRUE;
11830         goto realign;
11831       }
11832       return NULL;
11833     }
11834   }
11835 
11836   numleaves--;
11837 
11838   /// if key is NULL and check_key is TRUE, we will limit whay voidptr leaves can be added. This is necessary for layers
11839   /// since voidptrs which may be loaded can mess up the layer handling
11840   if (type == WEED_PLANT_LAYER) block_unk_ptrs = TRUE;
11841 
11842   while (numleaves--) {
11843     if ((err = weed_leaf_deserialise(fd, plant, NULL, mem, block_unk_ptrs)) != 0) {
11844       if (err != -5) {  // all except malloc error
11845         char *badfname = filename_from_fd(NULL, fd);
11846         char *msg = lives_strdup_printf("Data mismatch (%d) reading from\n%s", err, badfname ? badfname : "unknown file");
11847         LIVES_ERROR(msg);
11848         lives_free(msg);
11849         bugfd = fd;
11850       }
11851       if (newd) {
11852         weed_plant_free(plant);
11853         plant = NULL;
11854       }
11855       if (bugfd == fd && !retried) {
11856         retried = TRUE;
11857         goto realign;
11858       }
11859       return NULL;
11860     }
11861   }
11862 
11863   if ((type = weed_get_plant_type(plant)) == WEED_PLANT_UNKNOWN) {
11864     //g_print("badplant was %d\n", type);
11865     if (newd) weed_plant_free(plant);
11866     return NULL;
11867   }
11868   //g_print("goodplant %p\n", plant);
11869   bugfd = -1;
11870   return plant;
11871 }
11872 
11873 
write_filter_defaults(int fd,int idx)11874 boolean write_filter_defaults(int fd, int idx) {
11875   // return FALSE on write error
11876   char *hashname;
11877   weed_plant_t *filter = weed_filters[idx], **ptmpls;
11878   int num_params;
11879   int i;
11880   boolean wrote_hashname = FALSE;
11881   size_t vlen;
11882   int ntowrite = 0;
11883 
11884   ptmpls = weed_get_plantptr_array(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, &num_params);
11885   if (!ptmpls) return TRUE;
11886 
11887   for (i = 0; i < num_params; i++) {
11888     if (weed_plant_has_leaf(ptmpls[i], WEED_LEAF_HOST_DEFAULT)) {
11889       ntowrite++;
11890     }
11891   }
11892 
11893   for (i = 0; i < num_params; i++) {
11894     if (weed_plant_has_leaf(ptmpls[i], WEED_LEAF_HOST_DEFAULT)) {
11895       if (!wrote_hashname) {
11896         hashname = make_weed_hashname(idx, TRUE, FALSE, 0, FALSE);
11897         vlen = lives_strlen(hashname);
11898 
11899         lives_write_le_buffered(fd, &vlen, 4, TRUE);
11900         lives_write_buffered(fd, hashname, vlen, TRUE);
11901         lives_freep((void **)&hashname);
11902         wrote_hashname = TRUE;
11903         lives_write_le_buffered(fd, &ntowrite, 4, TRUE);
11904       }
11905       lives_write_le_buffered(fd, &i, 4, TRUE);
11906       weed_leaf_serialise(fd, ptmpls[i], WEED_LEAF_HOST_DEFAULT, FALSE, NULL);
11907     }
11908   }
11909   if (wrote_hashname) lives_write_buffered(fd, "\n", 1, TRUE);
11910 
11911   lives_freep((void **)&ptmpls);
11912 
11913   if (THREADVAR(write_failed) == fd + 1) {
11914     THREADVAR(write_failed) = 0;
11915     return FALSE;
11916   }
11917   return TRUE;
11918 }
11919 
11920 
read_filter_defaults(int fd)11921 boolean read_filter_defaults(int fd) {
11922   weed_plant_t *filter, **ptmpls;
11923   void *buf = NULL;
11924 
11925   size_t vlen;
11926 
11927   int vleni, vlenz;
11928   int i, pnum;
11929   int num_params = 0;
11930   int ntoread;
11931   boolean read_failed = FALSE;
11932   boolean found = FALSE;
11933 
11934   char *tmp;
11935 
11936 
11937   while (1) {
11938     if (lives_read_le_buffered(fd, &vleni, 4, TRUE) < 4) {
11939       break;
11940     }
11941 
11942     // some files erroneously used a vlen of 8
11943     if (lives_read_le_buffered(fd, &vlenz, 4, TRUE) < 4) {
11944       break;
11945     }
11946 
11947     vlen = (size_t)vleni;
11948 
11949     if (capable->byte_order == LIVES_BIG_ENDIAN && prefs->bigendbug) {
11950       if (vleni == 0 && vlenz != 0) vlen = (size_t)vlenz;
11951     } else {
11952       if (vlenz != 0) {
11953         if (lives_lseek_buffered_rdonly(fd, -4) < 0) return FALSE;
11954       }
11955     }
11956 
11957     if (vlen > MAX_WEED_STRLEN) return FALSE;
11958 
11959     buf = lives_malloc(vlen + 1);
11960     if (lives_read_buffered(fd, buf, vlen, TRUE) < vlen) break;
11961 
11962     lives_memset((char *)buf + vlen, 0, 1);
11963     for (i = 0; i < num_weed_filters; i++) {
11964       if (!lives_strcmp((char *)buf, (tmp = make_weed_hashname(i, TRUE, FALSE, 0, FALSE)))) {
11965         lives_free(tmp);
11966         found = TRUE;
11967         break;
11968       }
11969       lives_free(tmp);
11970     }
11971     if (!found) {
11972       for (i = 0; i < num_weed_filters; i++) {
11973         if (!lives_strcmp((char *)buf, (tmp = make_weed_hashname(i, TRUE, TRUE, 0, FALSE)))) {
11974           lives_free(tmp);
11975           break;
11976         }
11977         lives_free(tmp);
11978       }
11979     }
11980 
11981     lives_freep((void **)&buf);
11982 
11983     ptmpls = NULL;
11984 
11985     if (i < num_weed_filters) {
11986       filter = weed_filters[i];
11987       ptmpls = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, &num_params);
11988     } else num_params = 0;
11989 
11990     if (lives_read_le_buffered(fd, &ntoread, 4, TRUE) < 4) {
11991       if (ptmpls) lives_free(ptmpls);
11992       break;
11993     }
11994 
11995     for (i = 0; i < ntoread; i++) {
11996       if (lives_read_le_buffered(fd, &pnum, 4, TRUE) < 4) {
11997         if (ptmpls) lives_free(ptmpls);
11998         break;
11999       }
12000 
12001       if (pnum < num_params) {
12002         if (weed_leaf_deserialise(fd, ptmpls[pnum], WEED_LEAF_HOST_DEFAULT, NULL, FALSE) < 0) {
12003         }
12004       } else {
12005         weed_plant_t *dummyplant = weed_plant_new(WEED_PLANT_UNKNOWN);
12006         if (weed_leaf_deserialise(fd, dummyplant, WEED_LEAF_HOST_DEFAULT, NULL, FALSE) < 0) {
12007           weed_plant_free(dummyplant);
12008           read_failed = TRUE;
12009           break;
12010         }
12011       }
12012       if (ptmpls && weed_paramtmpl_value_irrelevant(ptmpls[pnum]))
12013         weed_leaf_delete(ptmpls[pnum], WEED_LEAF_HOST_DEFAULT);
12014     }
12015     if (read_failed) {
12016       if (ptmpls) lives_free(ptmpls);
12017       break;
12018     }
12019 
12020     buf = lives_malloc(strlen("\n"));
12021     lives_read_buffered(fd, buf, strlen("\n"), TRUE);
12022     lives_free(buf);
12023     if (ptmpls) lives_free(ptmpls);
12024     if (THREADVAR(read_failed) == fd + 1) {
12025       break;
12026     }
12027   }
12028 
12029   if (THREADVAR(read_failed) == fd + 1) {
12030     THREADVAR(read_failed) = 0;
12031     return FALSE;
12032   }
12033 
12034   return TRUE;
12035 }
12036 
12037 
write_generator_sizes(int fd,int idx)12038 boolean write_generator_sizes(int fd, int idx) {
12039   // TODO - handle optional channels
12040   // return FALSE on write error
12041   char *hashname;
12042   weed_plant_t *filter, **ctmpls;
12043   int num_channels;
12044   int i;
12045   size_t vlen;
12046   boolean wrote_hashname = FALSE;
12047 
12048   num_channels = enabled_in_channels(weed_filters[idx], FALSE);
12049   if (num_channels != 0) return TRUE;
12050 
12051   filter = weed_filters[idx];
12052 
12053   ctmpls = weed_get_plantptr_array_counted(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &num_channels);
12054   if (num_channels == 0) return TRUE;
12055 
12056   for (i = 0; i < num_channels; i++) {
12057     if (weed_plant_has_leaf(ctmpls[i], WEED_LEAF_HOST_WIDTH) || weed_plant_has_leaf(ctmpls[i], WEED_LEAF_HOST_HEIGHT) ||
12058         (!wrote_hashname && weed_plant_has_leaf(filter, WEED_LEAF_HOST_FPS))) {
12059       if (!wrote_hashname) {
12060         hashname = make_weed_hashname(idx, TRUE, FALSE, 0, FALSE);
12061         vlen = lives_strlen(hashname);
12062         lives_write_le_buffered(fd, &vlen, 4, TRUE);
12063         lives_write_buffered(fd, hashname, vlen, TRUE);
12064         lives_free(hashname);
12065         wrote_hashname = TRUE;
12066         if (weed_plant_has_leaf(filter, WEED_LEAF_HOST_FPS)) {
12067           int j = -1;
12068           lives_write_le_buffered(fd, &j, 4, TRUE);
12069           weed_leaf_serialise(fd, filter, WEED_LEAF_HOST_FPS, FALSE, NULL);
12070         }
12071       }
12072 
12073       lives_write_le_buffered(fd, &i, 4, TRUE);
12074       if (weed_plant_has_leaf(ctmpls[i], WEED_LEAF_HOST_WIDTH)) weed_leaf_serialise(fd, ctmpls[i],
12075             WEED_LEAF_HOST_WIDTH, FALSE, NULL);
12076       else weed_leaf_serialise(fd, ctmpls[i], WEED_LEAF_WIDTH, FALSE, NULL);
12077       if (weed_plant_has_leaf(ctmpls[i], WEED_LEAF_HOST_HEIGHT)) weed_leaf_serialise(fd, ctmpls[i],
12078             WEED_LEAF_HOST_HEIGHT, FALSE, NULL);
12079       else weed_leaf_serialise(fd, ctmpls[i], WEED_LEAF_HEIGHT, FALSE, NULL);
12080     }
12081   }
12082   if (wrote_hashname) lives_write_buffered(fd, "\n", 1, TRUE);
12083 
12084   if (THREADVAR(write_failed) == fd + 1) {
12085     THREADVAR(write_failed) = 0;
12086     return FALSE;
12087   }
12088   return TRUE;
12089 }
12090 
12091 
read_generator_sizes(int fd)12092 boolean read_generator_sizes(int fd) {
12093   weed_plant_t *filter, **ctmpls;
12094   ssize_t bytes;
12095   size_t vlen;
12096 
12097   char *buf;
12098   char *tmp;
12099 
12100   boolean found = FALSE;
12101   boolean ready;
12102 
12103   int vleni, vlenz;
12104   int i, num_chans = 0, cnum;
12105 
12106   while (1) {
12107     if (lives_read_le_buffered(fd, &vleni, 4, TRUE) < 4) {
12108       break;
12109     }
12110 
12111     // some files erroneously used a vlen of 8
12112     if (lives_read_le_buffered(fd, &vlenz, 4, TRUE) < 4) {
12113       break;
12114     }
12115 
12116     vlen = (size_t)vleni;
12117 
12118     if (capable->byte_order == LIVES_BIG_ENDIAN && prefs->bigendbug) {
12119       if (vleni == 0 && vlenz != 0) vlen = (size_t)vlenz;
12120     } else {
12121       if (vlenz != 0) {
12122         if (lives_lseek_buffered_rdonly(fd, -4) < 0) {
12123           return FALSE;
12124         }
12125       }
12126     }
12127 
12128     if (vlen > MAX_WEED_STRLEN) {
12129       return FALSE;
12130     }
12131 
12132     buf = (char *)lives_malloc(vlen + 1);
12133 
12134     bytes = lives_read_buffered(fd, buf, vlen, TRUE);
12135     if (bytes < vlen) {
12136       break;
12137     }
12138     lives_memset((char *)buf + vlen, 0, 1);
12139 
12140     for (i = 0; i < num_weed_filters; i++) {
12141       if (!lives_strcmp(buf, (tmp = make_weed_hashname(i, TRUE, FALSE, 0, FALSE)))) {
12142         lives_free(tmp);
12143         found = TRUE;
12144         break;
12145       }
12146       lives_free(tmp);
12147     }
12148     if (!found) {
12149       for (i = 0; i < num_weed_filters; i++) {
12150         if (!lives_strcmp(buf, (tmp = make_weed_hashname(i, TRUE, TRUE, 0, FALSE)))) {
12151           lives_free(tmp);
12152           break;
12153         }
12154         lives_free(tmp);
12155       }
12156     }
12157 
12158     lives_free(buf);
12159     ctmpls = NULL;
12160 
12161     ready = FALSE;
12162     filter = NULL;
12163     if (i < num_weed_filters) {
12164       filter = weed_filters[i];
12165       ctmpls = weed_get_plantptr_array_counted(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &num_chans);
12166     }
12167     while (!ready) {
12168       ready = TRUE;
12169       bytes = lives_read_le_buffered(fd, &cnum, 4, TRUE);
12170       if (bytes < 4) {
12171         break;
12172       }
12173       if (!filter) {
12174         // we still need to read past the values even if we didn't find the filter
12175         if (cnum == -1) {
12176           if (weed_leaf_deserialise(fd, NULL, WEED_LEAF_HOST_FPS, NULL, FALSE) < 0) break;
12177           ready = FALSE;
12178         } else {
12179           if (weed_leaf_deserialise(fd, NULL, WEED_LEAF_HOST_WIDTH, NULL, FALSE) < 0) break;
12180           if (weed_leaf_deserialise(fd, NULL, WEED_LEAF_HOST_HEIGHT, NULL, FALSE) < 0) break;
12181         }
12182       } else {
12183         if (cnum < num_chans && cnum >= 0) {
12184           if (weed_leaf_deserialise(fd, ctmpls[cnum], WEED_LEAF_HOST_WIDTH, NULL, FALSE) < 0) break;
12185           if (weed_leaf_deserialise(fd, ctmpls[cnum], WEED_LEAF_HOST_HEIGHT, NULL, FALSE) < 0) break;
12186           if (weed_get_int_value(ctmpls[cnum], WEED_LEAF_HOST_WIDTH, NULL) == 0)
12187             weed_set_int_value(ctmpls[cnum], WEED_LEAF_HOST_WIDTH, DEF_GEN_WIDTH);
12188           if (weed_get_int_value(ctmpls[cnum], WEED_LEAF_HOST_HEIGHT, NULL) == 0)
12189             weed_set_int_value(ctmpls[cnum], WEED_LEAF_HOST_HEIGHT, DEF_GEN_HEIGHT);
12190         } else if (cnum == -1) {
12191           if (weed_leaf_deserialise(fd, filter, WEED_LEAF_HOST_FPS, NULL, FALSE) < 0) break;
12192           ready = FALSE;
12193         }
12194       }
12195     }
12196 
12197     lives_freep((void **)&ctmpls);
12198 
12199     if (THREADVAR(read_failed) == fd + 1) {
12200       break;
12201     }
12202     buf = (char *)lives_malloc(strlen("\n"));
12203     lives_read_buffered(fd, buf, strlen("\n"), TRUE);
12204     lives_free(buf);
12205 
12206     if (THREADVAR(read_failed) == fd + 1) {
12207       break;
12208     }
12209   }
12210 
12211   if (THREADVAR(read_failed) == fd + 1) {
12212     THREADVAR(read_failed) = 0;
12213     return FALSE;
12214   }
12215   return TRUE;
12216 }
12217 
12218 
reset_frame_and_clip_index(void)12219 void reset_frame_and_clip_index(void) {
12220   if (!mainw->clip_index) {
12221     mainw->clip_index = (int *)lives_malloc(sizint);
12222     mainw->clip_index[0] = -1;
12223   }
12224   if (!mainw->frame_index) {
12225     mainw->frame_index = (int64_t *)lives_malloc(8);
12226     mainw->frame_index[0] = 0;
12227   }
12228 }
12229 
12230 // key/mode parameter defaults
12231 
read_key_defaults(int fd,int nparams,int key,int mode,int ver)12232 boolean read_key_defaults(int fd, int nparams, int key, int mode, int ver) {
12233   // read default param values for key/mode from file
12234 
12235   // if key < 0 we just read past the bytes in the file
12236 
12237   // return FALSE on EOF
12238 
12239   int i, j, nvals, nigns;
12240   int idx;
12241   ssize_t bytes;
12242   weed_plant_t *filter;
12243   int xnparams = 0, maxparams = nparams;
12244   weed_plant_t **key_defs;
12245   weed_timecode_t tc;
12246 
12247   int ret = 0;
12248 
12249   if (key >= 0) {
12250     idx = key_to_fx[key][mode];
12251     filter = weed_filters[idx];
12252     maxparams = xnparams = num_in_params(filter, FALSE, FALSE);
12253   }
12254 
12255   if (xnparams > maxparams) maxparams = xnparams;
12256   if (!maxparams) return FALSE;
12257 
12258   key_defs = (weed_plant_t **)lives_calloc(maxparams, sizeof(weed_plant_t *));
12259 
12260   for (i = 0; i < nparams; i++) {
12261     if (key < 0 || i < xnparams) {
12262       key_defs[i] = weed_plant_new(WEED_PLANT_PARAMETER);
12263     }
12264     if (ver > 1) {
12265       // for future - read nvals
12266       bytes = lives_read_le_buffered(fd, &nvals, 4, TRUE);
12267       if (bytes < 4) {
12268         goto err123;
12269       }
12270       if (!nvals) return TRUE;
12271       bytes = lives_read_le_buffered(fd, &tc, 8, TRUE);
12272       if (bytes < sizeof(weed_timecode_t)) {
12273         goto err123;
12274       }
12275       // read n ints (booleans)
12276       bytes = lives_read_le_buffered(fd, &nigns, 4, TRUE);
12277       if (bytes < 4) {
12278         goto err123;
12279       }
12280       if (nigns > 0) {
12281         int *igns = (int *)lives_malloc(nigns * 4);
12282         for (j = 0; j < nigns; j++) {
12283           bytes = lives_read_le_buffered(fd, &igns[j], 4, TRUE);
12284           if (bytes < 4) {
12285             goto err123;
12286           }
12287         }
12288         lives_free(igns);
12289       }
12290     }
12291 
12292     if (weed_leaf_deserialise(fd, key_defs[i], WEED_LEAF_VALUE, NULL, FALSE) < 0) goto err123;
12293 
12294     if (ver > 1) {
12295       for (j = 0; j < nvals; j++) {
12296         // for future - read timecodes
12297         weed_plant_t *plant = weed_plant_new(WEED_PLANT_PARAMETER);
12298         bytes = lives_read_le_buffered(fd, &tc, 8, TRUE);
12299         if (bytes < 8) {
12300           goto err123;
12301         }
12302         // read n ints (booleans)
12303         bytes = lives_read_le_buffered(fd, &nigns, 4, TRUE);
12304         if (bytes < 4) {
12305           goto err123;
12306         }
12307         if (nigns > 0) {
12308           int *igns = (int *)lives_malloc(nigns * 4);
12309           for (j = 0; j < nigns; j++) {
12310             bytes = lives_read_le_buffered(fd, &igns[j], 4, TRUE);
12311             if (bytes < 4) {
12312               goto err123;
12313             }
12314           }
12315           // discard excess values
12316           ret = weed_leaf_deserialise(fd, plant, WEED_LEAF_VALUE, NULL, FALSE);
12317           weed_plant_free(plant);
12318           if (ret < 0) goto err123;
12319 	  // *INDENT-OFF*
12320 	}}}}
12321   // *INDENT-ON*
12322 
12323   if (key >= 0) key_defaults[key][mode] = key_defs;
12324 
12325 err123:
12326   if (key < 0) {
12327     for (i = 0; i < nparams; i++) {
12328       if (key_defs[i]) weed_plant_free(key_defs[i]);
12329     }
12330     lives_free(key_defs);
12331   }
12332 
12333   if (ret < 0 || THREADVAR(read_failed) == fd + 1) {
12334     THREADVAR(read_failed) = 0;
12335     return FALSE;
12336   }
12337 
12338   return TRUE;
12339 }
12340 
12341 
apply_key_defaults(weed_plant_t * inst,int key,int mode)12342 void apply_key_defaults(weed_plant_t *inst, int key, int mode) {
12343   // call with filter_mutex locked
12344   // apply key/mode param defaults to a filter instance
12345   weed_plant_t **defs, **params;
12346   weed_plant_t *filter = weed_instance_get_filter(inst, TRUE);
12347   int nparams = num_in_params(filter, FALSE, FALSE);
12348   register int i, j = 0;
12349 
12350   if (nparams == 0) return;
12351 
12352   defs = key_defaults[key][mode];
12353   if (!defs) return;
12354 
12355   do {
12356     params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
12357     nparams = num_in_params(inst, FALSE, FALSE);
12358 
12359     for (i = 0; i < nparams; i++) {
12360       if (!is_hidden_param(inst, i))
12361         weed_leaf_dup(params[i], defs[j], WEED_LEAF_VALUE);
12362       j++;
12363     }
12364     lives_free(params);
12365   } while (weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, NULL));
12366 }
12367 
12368 
write_key_defaults(int fd,int key,int mode)12369 void write_key_defaults(int fd, int key, int mode) {
12370   // save key/mode param defaults to file
12371   // caller should check for write errors
12372 
12373   weed_plant_t *filter;
12374   weed_plant_t **key_defs;
12375   int nparams = 0;
12376 
12377   if ((key_defs = key_defaults[key][mode]) == NULL) {
12378     lives_write_le_buffered(fd, &nparams, 4, TRUE);
12379     return;
12380   }
12381 
12382   filter = weed_filters[key_to_fx[key][mode]];
12383   nparams = num_in_params(filter, FALSE, FALSE);
12384   lives_write_le_buffered(fd, &nparams, 4, TRUE);
12385 
12386   for (int i = 0; i < nparams; i++) {
12387     if (THREADVAR(write_failed) == fd + 1) {
12388       THREADVAR(write_failed) = 0;
12389       break;
12390     }
12391     weed_leaf_serialise(fd, key_defs[i], WEED_LEAF_VALUE, FALSE, NULL);
12392   }
12393 }
12394 
12395 
free_key_defaults(int key,int mode)12396 void free_key_defaults(int key, int mode) {
12397   // free key/mode param defaults
12398   weed_plant_t *filter;
12399   weed_plant_t **key_defs;
12400   int nparams;
12401 
12402   if (key >= FX_KEYS_MAX_VIRTUAL || mode >= prefs->max_modes_per_key) return;
12403 
12404   key_defs = key_defaults[key][mode];
12405 
12406   if (!key_defs) return;
12407 
12408   filter = weed_filters[key_to_fx[key][mode]];
12409   nparams = num_in_params(filter, FALSE, FALSE);
12410 
12411   for (int i = 0; i < nparams; i++) if (key_defs[i]) weed_plant_free(key_defs[i]);
12412 
12413   lives_free(key_defaults[key][mode]);
12414   key_defaults[key][mode] = NULL;
12415 }
12416 
12417 
set_key_defaults(weed_plant_t * inst,int key,int mode)12418 void set_key_defaults(weed_plant_t *inst, int key, int mode) {
12419   // copy key/mode param defaults from an instance
12420   weed_plant_t **key_defs, **params;
12421   weed_plant_t *filter = weed_instance_get_filter(inst, TRUE);
12422   int i = -1;
12423   int nparams, poffset = 0;
12424 
12425   if (key_defaults[key][mode]) free_key_defaults(key, mode);
12426 
12427   nparams = num_in_params(filter, FALSE, FALSE);
12428   if (nparams == 0) return;
12429 
12430   key_defs = (weed_plant_t **)lives_malloc(nparams * sizeof(weed_plant_t *));
12431 
12432   do {
12433     nparams = num_in_params(inst, FALSE, FALSE);
12434     if (nparams > 0) {
12435       int last = nparams + poffset - 1;
12436       params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
12437       while (i < last) {
12438         key_defs[++i] = weed_plant_new(WEED_PLANT_PARAMETER);
12439         weed_leaf_dup(key_defs[i], params[i - poffset], WEED_LEAF_VALUE);
12440       }
12441       lives_free(params);
12442       poffset = ++last;
12443     }
12444   } while ((inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, NULL)));
12445 
12446   key_defaults[key][mode] = key_defs;
12447 }
12448 
12449 
has_key_defaults(void)12450 boolean has_key_defaults(void) {
12451   // check if any key/mode has default parameters set
12452   for (int i = 0; i < prefs->rte_keys_virtual; i++) {
12453     for (int j = 0; j < prefs->max_modes_per_key; j++) {
12454       if (key_defaults[i][j]) return TRUE;
12455     }
12456   }
12457   return FALSE;
12458 }
12459 
12460 
12461