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 **)¶ms);
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