1 // effects.c
2 // LiVES (lives-exe)
3 // (c) G. Finch <salsaman+lives@gmail.com> 2003 - 2020
4 // Released under the GPL 3 or later
5 // see file ../COPYING for licensing details
6 
7 #include "main.h"
8 
9 #include <fcntl.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13 
14 #include "effects.h"
15 #include "effects-weed.h"
16 #include "interface.h"
17 #include "paramwindow.h"
18 #include "cvirtual.h"
19 #include "resample.h"
20 #include "ce_thumbs.h"
21 #include "callbacks.h"
22 
23 //////////// Effects ////////////////
24 
25 #ifdef HAVE_YUV4MPEG
26 #include "lives-yuv4mpeg.h"
27 #endif
28 
29 #include "rte_window.h"
30 
31 static weed_plant_t *resize_instance = NULL;
32 
33 static boolean apply_audio_fx;
34 
35 ///////////////////////////////////////////////////
36 
37 // generic
38 
39 
lives_fx_cat_to_text(lives_fx_cat_t cat,boolean plural)40 char *lives_fx_cat_to_text(lives_fx_cat_t cat, boolean plural) {
41   // return value should be free'd after use
42   switch (cat) {
43   // main categories
44   case LIVES_FX_CAT_VIDEO_GENERATOR:
45     if (!plural) return ((_("generator")));
46     else return ((_("Generators")));
47   case LIVES_FX_CAT_AUDIO_GENERATOR:
48     if (!plural) return ((_("audio generator")));
49     else return ((_("Audio Generators")));
50   case LIVES_FX_CAT_AV_GENERATOR:
51     if (!plural) return ((_("audio/video generator")));
52     else return ((_("Audio/Video Generators")));
53   case LIVES_FX_CAT_DATA_GENERATOR:
54     if (!plural) return ((_("data generator")));
55     else return ((_("Data Generators")));
56   case LIVES_FX_CAT_DATA_VISUALISER:
57     if (!plural) return ((_("data visualiser")));
58     else return ((_("Data Visualisers")));
59   case LIVES_FX_CAT_DATA_PROCESSOR:
60     if (!plural) return ((_("data processor")));
61     else return ((_("Data Processors")));
62   case LIVES_FX_CAT_DATA_SOURCE:
63     if (!plural) return ((_("data source")));
64     else return ((_("Data Sources")));
65   case LIVES_FX_CAT_TRANSITION:
66     if (!plural) return ((_("transition")));
67     else return ((_("Transitions")));
68   case LIVES_FX_CAT_EFFECT:
69     if (!plural) return ((_("effect")));
70     else return ((_("Effects")));
71   case LIVES_FX_CAT_UTILITY:
72     if (!plural) return ((_("utility")));
73     else return ((_("Utilities")));
74   case LIVES_FX_CAT_COMPOSITOR:
75     if (!plural) return ((_("compositor")));
76     else return ((_("Compositors")));
77   case LIVES_FX_CAT_TAP:
78     if (!plural) return ((_("tap")));
79     else return ((_("Taps")));
80   case LIVES_FX_CAT_SPLITTER:
81     if (!plural) return ((_("splitter")));
82     else return ((_("Splitters")));
83   case LIVES_FX_CAT_CONVERTER:
84     if (!plural) return ((_("converter")));
85     else return ((_("Converters")));
86   case LIVES_FX_CAT_ANALYSER:
87     if (!plural) return ((_("analyser")));
88     else return ((_("Analysers")));
89 
90   // subcategories
91   case LIVES_FX_CAT_AV_TRANSITION:
92     if (!plural) return ((_("audio/video")));
93     else return ((_("Audio/Video Transitions")));
94   case LIVES_FX_CAT_VIDEO_TRANSITION:
95     if (!plural) return ((_("video only")));
96     else return ((_("Video only Transitions")));
97   case LIVES_FX_CAT_AUDIO_TRANSITION:
98     if (!plural) return ((_("audio only")));
99     else return ((_("Audio only Transitions")));
100   case LIVES_FX_CAT_AUDIO_MIXER:
101     if (!plural) return ((_("audio")));
102     else return ((_("Audio Mixers")));
103   case LIVES_FX_CAT_AUDIO_EFFECT:
104     if (!plural) return ((_("audio")));
105     else return ((_("Audio Effects")));
106   case LIVES_FX_CAT_VIDEO_EFFECT:
107     if (!plural) return ((_("video")));
108     else return ((_("Video Effects")));
109   case LIVES_FX_CAT_AUDIO_VOL:
110     if (!plural) return ((_("audio volume controller")));
111     else return ((_("Audio Volume Controllers")));
112   case LIVES_FX_CAT_VIDEO_ANALYSER:
113     if (!plural) return ((_("video analyser")));
114     else return ((_("Video Analysers")));
115   case LIVES_FX_CAT_AUDIO_ANALYSER:
116     if (!plural) return ((_("audio analyser")));
117     else return ((_("Audio Analysers")));
118 
119   default:
120     return ((_("unknown")));
121   }
122 }
123 
124 
125 // Rendered effects
126 
do_effect(lives_rfx_t * rfx,boolean is_preview)127 boolean do_effect(lives_rfx_t *rfx, boolean is_preview) {
128   // apply a rendered effect to the current file
129 
130   // returns FALSE if the user cancelled
131   // leave_info_file is set if a preview turned into actual processing: ie. no params were changed after the preview
132   // preview generates .pre files instead of .mgk, so needs special post-processing
133 
134   int oundo_start = cfile->undo_start;
135   int oundo_end = cfile->undo_end;
136   char effectstring[128];
137   double old_pb_fps = cfile->pb_fps;
138 
139   char *text;
140   char *fxcommand = NULL, *cmd;
141   int current_file = mainw->current_file;
142 
143   int new_file = current_file;
144   int ldfile;
145 
146   boolean got_no_frames = FALSE;
147   char *tmp;
148 
149   if (!CURRENT_CLIP_IS_VALID) return FALSE;
150 
151   if (rfx->num_in_channels == 0 && !is_preview) current_file = mainw->pre_src_file;
152 
153   if (is_preview) {
154     // generators start at 1, even though they have no initial frames
155     cfile->progress_start = cfile->undo_start = cfile->start;
156     cfile->progress_end = cfile->undo_end = cfile->end;
157   } else if (rfx->num_in_channels != 2) {
158     cfile->progress_start = cfile->undo_start = cfile->start;
159     cfile->progress_end = cfile->undo_end = cfile->end;
160   }
161 
162   if (!mainw->internal_messaging && !mainw->keep_pre) {
163     char *pdefault;
164     char *plugin_name;
165 
166     if (rfx->status == RFX_STATUS_BUILTIN) plugin_name = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR,
167           PLUGIN_RENDERED_EFFECTS_BUILTIN, rfx->name, NULL);
168     else plugin_name = lives_strdup(rfx->name);
169 
170     if (rfx->num_in_channels == 2) {
171       // transition has a few extra bits
172       pdefault = lives_strdup_printf("%s %d %d %d %d %d %s %f %s %d \"%s/%s\"", cfile->handle, rfx->status,
173                                      cfile->progress_start, cfile->progress_end, cfile->hsize, cfile->vsize,
174                                      get_image_ext_for_type(cfile->img_type), cfile->fps,
175                                      get_image_ext_for_type(clipboard->img_type),
176                                      clipboard->start, prefs->workdir, clipboard->handle);
177     } else {
178       pdefault = lives_strdup_printf("%s %d %d %d %d %d %s %f", cfile->handle, rfx->status, cfile->progress_start,
179                                      cfile->progress_end, cfile->hsize, cfile->vsize, get_image_ext_for_type(cfile->img_type), cfile->fps);
180     }
181     // and append params
182     if (is_preview) {
183       cmd = lives_strdup("pfxrender");
184       mainw->show_procd = FALSE;
185     } else cmd = lives_strdup("fxrender");
186     fxcommand = lives_strconcat(prefs->backend, " \"", cmd, "_", plugin_name, "\" ", pdefault,
187                                 (tmp = param_marshall(rfx, FALSE)), NULL);
188     lives_free(plugin_name);
189     lives_free(cmd);
190     lives_free(pdefault);
191     lives_free(tmp);
192   }
193 
194   if (!mainw->keep_pre) lives_rm(cfile->info_file);
195 
196   if (!mainw->internal_messaging && !mainw->keep_pre) {
197     if (cfile->frame_index_back) {
198       lives_free(cfile->frame_index_back);
199       cfile->frame_index_back = NULL;
200     }
201     lives_system(fxcommand, FALSE);
202     lives_free(fxcommand);
203   } else {
204     if (mainw->num_tr_applied > 0 && mainw->blend_file > 0 && mainw->files[mainw->blend_file] &&
205         mainw->files[mainw->blend_file]->clip_type != CLIP_TYPE_GENERATOR) {
206       mainw->files[mainw->blend_file]->frameno = mainw->files[mainw->blend_file]->start - 1;
207     }
208   }
209   mainw->effects_paused = FALSE;
210 
211   if (cfile->clip_type == CLIP_TYPE_FILE && rfx->status != RFX_STATUS_WEED) {
212     // start decoding frames for the rendered effect plugins to start processing
213     cfile->fx_frame_pump = cfile->start;
214     if (!cfile->pumper) {
215       cfile->pumper = lives_proc_thread_create(LIVES_THRDATTR_NONE, (lives_funcptr_t)virtual_to_images,
216                       -1, "iiibV", mainw->current_file,
217                       cfile->undo_start, cfile->undo_end, FALSE, NULL);
218     }
219   } else cfile->fx_frame_pump = 0;
220 
221   if (is_preview) {
222     cfile->undo_start = oundo_start;
223     cfile->undo_end = oundo_end;
224     mainw->current_file = current_file;
225     return TRUE;
226   }
227 
228   if (rfx->props & RFX_PROPS_MAY_RESIZE) {
229     tmp = (_("%s all frames..."));
230     text = lives_strdup_printf(tmp, _(rfx->action_desc));
231   } else {
232     if (rfx->num_in_channels == 2) {
233       tmp = (_("%s clipboard into frames %d to %d..."));
234       text = lives_strdup_printf(tmp, _(rfx->action_desc), cfile->progress_start, cfile->progress_end);
235     } else {
236       if (rfx->num_in_channels == 0) {
237         mainw->no_switch_dprint = TRUE;
238         if (mainw->gen_to_clipboard) {
239           tmp = (_("%s to clipboard..."));
240           text = lives_strdup_printf(tmp, _(rfx->action_desc));
241         } else {
242           tmp = (_("%s to new clip..."));
243           text = lives_strdup_printf(tmp, _(rfx->action_desc));
244         }
245       } else {
246         tmp = (_("%s frames %d to %d..."));
247         text = lives_strdup_printf(tmp, _(rfx->action_desc), cfile->start, cfile->end);
248       }
249     }
250   }
251 
252   if (!mainw->no_switch_dprint) d_print(""); // force switch text
253   ldfile = mainw->last_dprint_file;
254 
255   d_print(text);
256   lives_free(text);
257   lives_free(tmp);
258   mainw->last_dprint_file = ldfile;
259 
260   cfile->redoable = cfile->undoable = FALSE;
261   lives_widget_set_sensitive(mainw->redo, FALSE);
262   lives_widget_set_sensitive(mainw->undo, FALSE);
263 
264   cfile->undo_action = UNDO_EFFECT;
265 
266   if (rfx->props & RFX_PROPS_MAY_RESIZE) {
267     cfile->ohsize = cfile->hsize;
268     cfile->ovsize = cfile->vsize;
269     mainw->resizing = TRUE;
270     cfile->nokeep = TRUE;
271   }
272 
273   // 'play' as fast as we possibly can
274   cfile->pb_fps = 1000000.;
275 
276   if (rfx->num_in_channels == 2) {
277     tmp = (_("%s clipboard with selection"));
278     lives_snprintf(effectstring, 128, tmp, _(rfx->action_desc));
279   } else if (rfx->num_in_channels == 0) {
280     if (mainw->gen_to_clipboard) {
281       tmp = (_("%s to clipboard"));
282       lives_snprintf(effectstring, 128, tmp, _(rfx->action_desc));
283     } else {
284       tmp = (_("%s to new clip"));
285       lives_snprintf(effectstring, 128, tmp, _(rfx->action_desc));
286     }
287   } else {
288     tmp = (_("%s frames %d to %d"));
289     lives_snprintf(effectstring, 128, tmp, _(rfx->action_desc), cfile->undo_start, cfile->undo_end);
290   }
291   lives_free(tmp);
292 
293   if (rfx->props & RFX_PROPS_MAY_RESIZE || rfx->num_in_channels == 0) {
294     if (rfx->status == RFX_STATUS_WEED) {
295       // set out_channel dimensions for resizers / generators
296       int error;
297       weed_plant_t *first_out = get_enabled_channel((weed_plant_t *)rfx->source, 0, FALSE);
298       weed_plant_t *first_ot = weed_get_plantptr_value(first_out, WEED_LEAF_TEMPLATE, &error);
299       weed_set_int_value(first_out, WEED_LEAF_WIDTH, weed_get_int_value(first_ot, WEED_LEAF_HOST_WIDTH, &error));
300       weed_set_int_value(first_out, WEED_LEAF_HEIGHT, weed_get_int_value(first_ot, WEED_LEAF_HOST_HEIGHT, &error));
301     }
302   }
303 
304   if (!do_progress_dialog(TRUE, TRUE, effectstring) || mainw->error) {
305     if (cfile->pumper) {
306       lives_nanosleep_until_nonzero(lives_proc_thread_cancel(cfile->pumper));
307       cfile->pumper = NULL;
308     }
309     mainw->last_dprint_file = ldfile;
310     mainw->show_procd = TRUE;
311     mainw->keep_pre = FALSE;
312     if (mainw->error) {
313       widget_opts.non_modal = TRUE;
314       if (mainw->cancelled != CANCEL_ERROR) do_error_dialog(mainw->msg);
315       d_print_failed();
316       mainw->last_dprint_file = ldfile;
317     }
318     widget_opts.non_modal = FALSE;
319     if (mainw->cancelled != CANCEL_KEEP) {
320       cfile->undo_start = oundo_start;
321       cfile->undo_end = oundo_end;
322     }
323     cfile->pb_fps = old_pb_fps;
324     mainw->internal_messaging = FALSE;
325     mainw->resizing = FALSE;
326     cfile->nokeep = FALSE;
327     cfile->fx_frame_pump = 0;
328 
329     if (cfile->start == 0) {
330       cfile->start = 1;
331       cfile->end = cfile->frames;
332     }
333 
334     if (rfx->num_in_channels == 0 && mainw->current_file != current_file) {
335       mainw->suppress_dprint = TRUE;
336       close_current_file(current_file);
337       mainw->suppress_dprint = FALSE;
338     } else {
339       mainw->current_file = current_file;
340       do_rfx_cleanup(rfx);
341       if (!mainw->multitrack) showclipimgs();
342     }
343 
344     mainw->is_generating = FALSE;
345     mainw->no_switch_dprint = FALSE;
346 
347     if (mainw->multitrack) {
348       mainw->pre_src_file = -1;
349     }
350 
351     return FALSE;
352   }
353 
354   if (cfile->start == 0) {
355     cfile->start = 1;
356     cfile->end = cfile->frames;
357   }
358 
359   do_rfx_cleanup(rfx);
360 
361   mainw->resizing = FALSE;
362   cfile->nokeep = FALSE;
363   cfile->fx_frame_pump = 0;
364 
365   if (!mainw->gen_to_clipboard) {
366     lives_widget_set_sensitive(mainw->undo, TRUE);
367     if (rfx->num_in_channels > 0) cfile->undoable = TRUE;
368     cfile->pb_fps = old_pb_fps;
369     mainw->internal_messaging = FALSE;
370     if (rfx->num_in_channels > 0) lives_widget_set_sensitive(mainw->select_last, TRUE);
371     if (rfx->num_in_channels > 0) set_undoable(rfx->menu_text, TRUE);
372   }
373 
374   mainw->show_procd = TRUE;
375 
376   if (rfx->status != RFX_STATUS_WEED) {
377     int numtok = get_token_count(mainw->msg, '|');
378     if (numtok > 1) {
379       char **array = lives_strsplit(mainw->msg, "|", numtok);
380       // [0] is "completed"
381       if (numtok > 4) cfile->end = cfile->progress_end = cfile->start + atoi(array[4]) - 1;
382       if (rfx->props & RFX_PROPS_MAY_RESIZE || rfx->num_in_channels == 0) {
383         // get new frame size
384         uint64_t verhash = make_version_hash(rfx->rfx_version);
385         if (verhash >= 1008003) {
386           cfile->hsize = atoi(array[1]);
387           cfile->vsize = atoi(array[2]);
388         } else {
389           cfile->hsize = atoi(array[5]);
390           cfile->vsize = atoi(array[6]);
391         }
392         if (rfx->num_in_channels == 0) {
393           cfile->fps = cfile->pb_fps = strtod(array[3], NULL);
394           if (cfile->fps == 0.) cfile->fps = cfile->pb_fps = prefs->default_fps;
395           cfile->end = cfile->frames = atoi(array[4]);
396           cfile->bpp = cfile->img_type == IMG_TYPE_JPEG ? 24 : 32;
397         }
398       }
399       lives_strfreev(array);
400     }
401     if (rfx->num_in_channels == 0) {
402       cfile->progress_start = 1;
403       cfile->progress_end = cfile->frames;
404     }
405   } else {
406     int error;
407     weed_plant_t *first_out = get_enabled_channel((weed_plant_t *)rfx->source, 0, FALSE);
408     weed_plant_t *first_ot = weed_get_plantptr_value(first_out, WEED_LEAF_TEMPLATE, &error);
409     cfile->hsize = weed_get_int_value(first_ot, WEED_LEAF_HOST_WIDTH, &error);
410     cfile->vsize = weed_get_int_value(first_ot, WEED_LEAF_HOST_HEIGHT, &error);
411   }
412 
413   if (rfx->num_in_channels > 0) {
414     if (cfile->hsize == cfile->ohsize && cfile->vsize == cfile->ovsize) cfile->undo_action = UNDO_EFFECT;
415     else {
416       boolean bad_header = FALSE;
417       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_WIDTH, &cfile->hsize)) bad_header = TRUE;
418       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_HEIGHT, &cfile->vsize)) bad_header = TRUE;
419       cfile->undo_action = UNDO_RESIZABLE;
420       if (bad_header) do_header_write_error(mainw->current_file);
421     }
422   }
423 
424   mainw->cancelled = CANCEL_NONE;
425   mainw->error = FALSE;
426 
427   if (mainw->keep_pre) {
428     // this comes from a preview which then turned into processing
429     // the processing is the same as usual, except we use a different file extension (.pre instead of .mgk)
430     char *com = lives_strdup_printf("%s mv_pre \"%s\" %d %d \"%s\"", prefs->backend_sync, cfile->handle, cfile->progress_start,
431                                     cfile->progress_end, get_image_ext_for_type(cfile->img_type));
432 
433     lives_rm(cfile->info_file);
434     mainw->cancelled = CANCEL_NONE;
435     lives_system(com, FALSE);
436     lives_free(com);
437     mainw->keep_pre = FALSE;
438 
439     check_backend_return(cfile);
440 
441     if (mainw->error) {
442       if (!mainw->cancelled) {
443         widget_opts.non_modal = TRUE;
444         do_info_dialog(_("\nNo frames were generated.\n"));
445         widget_opts.non_modal = FALSE;
446         d_print_failed();
447       } else if (mainw->cancelled != CANCEL_ERROR) d_print_cancelled();
448       else d_print_failed();
449 
450       if (rfx->num_in_channels == 0) {
451         mainw->is_generating = FALSE;
452 
453         if (mainw->current_file != current_file) {
454           mainw->suppress_dprint = TRUE;
455           close_current_file(current_file);
456           mainw->suppress_dprint = FALSE;
457         }
458 
459         mainw->current_file = current_file;
460         mainw->last_dprint_file = ldfile;
461 
462         if (mainw->multitrack) {
463           mainw->current_file = mainw->multitrack->render_file;
464         }
465       }
466       mainw->no_switch_dprint = FALSE;
467       return FALSE;
468     }
469   }
470 
471   if (rfx->num_in_channels == 0) {
472     if (rfx->props & RFX_PROPS_BATCHG) {
473       // batch mode generators need some extra processing
474       char *imgdir = lives_build_path(prefs->workdir, cfile->handle, NULL);
475       int img_file = mainw->current_file;
476 
477       mainw->suppress_dprint = TRUE;
478       open_file_sel(imgdir, 0, 0);
479       lives_free(imgdir);
480       new_file = mainw->current_file;
481 
482       if (new_file != img_file) {
483         mainw->current_file = img_file;
484 
485         lives_snprintf(mainw->files[new_file]->name, CLIP_NAME_MAXLEN, "%s", cfile->name);
486         lives_snprintf(mainw->files[new_file]->file_name, PATH_MAX, "%s", cfile->file_name);
487 
488         lives_menu_item_set_text(mainw->files[new_file]->menuentry, cfile->name, FALSE);
489 
490         mainw->files[new_file]->fps = mainw->files[new_file]->pb_fps = cfile->fps;
491       } else got_no_frames = TRUE;
492 
493       close_current_file(current_file);
494       mainw->suppress_dprint = FALSE;
495 
496       if (!got_no_frames) mainw->current_file = new_file;
497     } else {
498       // TODO - use check_clip_intergity()
499       char *tfile = make_image_file_name(cfile, cfile->frames, get_image_ext_for_type(cfile->img_type));
500 
501       if (!lives_file_test(tfile, LIVES_FILE_TEST_EXISTS)) {
502         cfile->frames = get_frame_count(mainw->current_file, 1);
503         cfile->end = cfile->frames;
504       }
505       lives_free(tfile);
506     }
507 
508     if (got_no_frames || cfile->frames <= 0) {
509       mainw->is_generating = FALSE;
510       if (!mainw->cancelled) {
511         widget_opts.non_modal = TRUE;
512         do_info_dialog(_("\nNo frames were generated.\n"));
513         widget_opts.non_modal = FALSE;
514         d_print_failed();
515       } else d_print_cancelled();
516       if (!got_no_frames) {
517         mainw->suppress_dprint = TRUE;
518         close_current_file(current_file);
519         mainw->suppress_dprint = FALSE;
520       }
521       mainw->last_dprint_file = ldfile;
522       mainw->no_switch_dprint = FALSE;
523       if (mainw->multitrack) mainw->current_file = mainw->multitrack->render_file;
524       return FALSE;
525     }
526 
527     if (mainw->gen_to_clipboard) {
528       // here we will copy all values to the clipboard, including the handle
529       // then close the current file without deleting the frames
530 
531       init_clipboard();
532 
533       lives_memcpy(clipboard, cfile, sizeof(lives_clip_t));
534       cfile->is_loaded = TRUE;
535       mainw->suppress_dprint = TRUE;
536       mainw->close_keep_frames = TRUE;
537 
538       close_current_file(current_file);
539 
540       mainw->suppress_dprint = FALSE;
541       mainw->close_keep_frames = FALSE;
542 
543       new_file = current_file;
544 
545       mainw->untitled_number--;
546     } else {
547       if (!(rfx->props & RFX_PROPS_BATCHG)) {
548         // gen to new file
549         cfile->is_loaded = TRUE;
550         add_to_clipmenu();
551         if (!save_clip_values(new_file)) {
552           close_current_file(current_file);
553           return FALSE;
554         }
555 
556         if (prefs->crash_recovery) add_to_recovery_file(cfile->handle);
557 
558         if (mainw->multitrack) {
559           mt_init_clips(mainw->multitrack, mainw->current_file, TRUE);
560           mt_clip_select(mainw->multitrack, TRUE);
561         }
562 
563       }
564       lives_notify(LIVES_OSC_NOTIFY_CLIP_OPENED, "");
565     }
566     mainw->is_generating = FALSE;
567   }
568 
569   if (!mainw->gen_to_clipboard) cfile->changed = TRUE;
570   if (!mainw->multitrack) {
571     if (new_file != -1) {
572       lives_sync(1);
573       switch_clip(1, new_file, TRUE);
574     }
575   } else {
576     mainw->current_file = mainw->multitrack->render_file;
577     mainw->pre_src_file = -1;
578   }
579 
580   d_print_done();
581   mainw->no_switch_dprint = FALSE;
582   mainw->gen_to_clipboard = FALSE;
583   mainw->last_dprint_file = ldfile;
584 
585   return TRUE;
586 }
587 
588 
589 // realtime fx
590 
realfx_progress(boolean reset)591 lives_render_error_t realfx_progress(boolean reset) {
592   static lives_render_error_t write_error;
593 
594   LiVESError *error = NULL;
595 
596   char oname[PATH_MAX];
597 
598   LiVESPixbuf *pixbuf;
599 
600   ticks_t frameticks;
601 
602   weed_layer_t *layer;
603 
604   char *com, *tmp;
605 
606   static int i;
607 
608   int weed_error;
609   int layer_palette;
610   int retval;
611 
612   // this is called periodically from do_processing_dialog for internal effects
613 
614   if (reset) {
615     i = cfile->start;
616     clear_mainw_msg();
617 
618     if (cfile->clip_type == CLIP_TYPE_FILE) {
619       if (cfile->frame_index_back) lives_free(cfile->frame_index_back);
620       cfile->frame_index_back = frame_index_copy(cfile->frame_index, cfile->frames, 0);
621     }
622     write_error = LIVES_RENDER_ERROR_NONE;
623     return LIVES_RENDER_READY;
624   }
625 
626   if (mainw->effects_paused) return LIVES_RENDER_EFFECTS_PAUSED;
627 
628   // sig_progress...
629   lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%d", i);
630   // load, effect, save frame
631 
632   // skip resizing virtual frames
633   if (resize_instance && is_virtual_frame(mainw->current_file, i)) {
634     if (++i > cfile->end) {
635       mainw->internal_messaging = FALSE;
636       lives_snprintf(mainw->msg, 9, "completed");
637       return LIVES_RENDER_COMPLETE;
638     }
639     return LIVES_RENDER_PROCESSING;
640   }
641 
642   if (has_video_filters(FALSE) || resize_instance) {
643     frameticks = (i - cfile->start + 1.) / cfile->fps * TICKS_PER_SECOND;
644     THREADVAR(rowstride_alignment_hint) = 4;
645     layer = lives_layer_new_for_frame(mainw->current_file, i);
646     if (!pull_frame(layer, get_image_ext_for_type(cfile->img_type), frameticks)) {
647       // do_read_failed_error_s() cannot be used here as we dont know the filename
648       lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "error|missing image %d", i);
649       return LIVES_RENDER_WARNING_READ_FRAME;
650     }
651 
652     layer = on_rte_apply(layer, 0, 0, (weed_timecode_t)frameticks);
653 
654     if (!has_video_filters(TRUE) || resize_instance) {
655       layer_palette = weed_get_int_value(layer, WEED_LEAF_CURRENT_PALETTE, &weed_error);
656 
657       if (!resize_instance) resize_layer(layer, cfile->hsize, cfile->vsize, LIVES_INTERP_BEST, layer_palette, 0);
658       if (cfile->img_type == IMG_TYPE_JPEG && layer_palette != WEED_PALETTE_RGB24 && layer_palette != WEED_PALETTE_RGBA32) {
659         convert_layer_palette(layer, WEED_PALETTE_RGB24, 0);
660         layer_palette = WEED_PALETTE_RGB24;
661       } else if (cfile->img_type == IMG_TYPE_PNG && layer_palette != WEED_PALETTE_RGBA32) {
662         convert_layer_palette(layer, WEED_PALETTE_RGBA32, 0);
663         layer_palette = WEED_PALETTE_RGBA32;
664       }
665 
666       pixbuf = layer_to_pixbuf(layer, TRUE, FALSE);
667       weed_plant_free(layer);
668 
669       tmp = make_image_file_name(cfile, i, LIVES_FILE_EXT_MGK);
670       lives_snprintf(oname, PATH_MAX, "%s", tmp);
671       lives_free(tmp);
672 
673       do {
674         retval = 0;
675         lives_pixbuf_save(pixbuf, oname, cfile->img_type, 100, cfile->hsize, cfile->vsize, &error);
676 
677         if (error) {
678           retval = do_write_failed_error_s_with_retry(oname, error->message);
679           lives_error_free(error);
680           error = NULL;
681           if (retval != LIVES_RESPONSE_RETRY) write_error = LIVES_RENDER_ERROR_WRITE_FRAME;
682         }
683       } while (retval == LIVES_RESPONSE_RETRY);
684 
685       lives_widget_object_unref(pixbuf);
686 
687       if (cfile->clip_type == CLIP_TYPE_FILE) {
688         cfile->frame_index[i - 1] = -1;
689       }
690     } else weed_plant_free(layer);
691   }
692   if (apply_audio_fx) {
693     if (!apply_rte_audio((double)cfile->arate / (double)cfile->fps + (double)rand() / .5 / (double)(RAND_MAX))) {
694       return LIVES_RENDER_ERROR_WRITE_AUDIO;
695     }
696   }
697 
698   if (++i > cfile->end) {
699     if (resize_instance || (has_video_filters(FALSE) && !has_video_filters(TRUE))) {
700       mainw->error = FALSE;
701       mainw->cancelled = CANCEL_NONE;
702       com = lives_strdup_printf("%s mv_mgk \"%s\" %d %d \"%s\"", prefs->backend, cfile->handle, cfile->start,
703                                 cfile->end, get_image_ext_for_type(cfile->img_type));
704       lives_system(com, FALSE);
705       lives_free(com);
706       mainw->internal_messaging = FALSE;
707 
708       check_backend_return(cfile);
709 
710       if (mainw->error) write_error = LIVES_RENDER_ERROR_WRITE_FRAME;
711       //cfile->may_be_damaged=TRUE;
712       else {
713         if (cfile->clip_type == CLIP_TYPE_FILE) {
714           if (!check_if_non_virtual(mainw->current_file, 1, cfile->frames)) save_frame_index(mainw->current_file);
715         }
716         return LIVES_RENDER_COMPLETE;
717       }
718     } else {
719       sprintf(mainw->msg, "%s", "completed");
720       return LIVES_RENDER_COMPLETE;
721     }
722   }
723   if (write_error) return write_error;
724   return LIVES_RENDER_PROCESSING;
725 }
726 
727 
on_realfx_activate_inner(int type,lives_rfx_t * rfx)728 boolean on_realfx_activate_inner(int type, lives_rfx_t *rfx) {
729   // type can be 0 - apply current realtime effects
730   // 1 - resize (using weed filter)
731   boolean retval;
732 
733   boolean has_new_audio = FALSE;
734 
735   apply_audio_fx = FALSE;
736 
737   if (type == 0 && ((cfile->achans > 0 && prefs->audio_src == AUDIO_SRC_INT && has_audio_filters(AF_TYPE_ANY)) ||
738                     mainw->agen_key != 0)) {
739     if (mainw->agen_key != 0 && cfile->achans == 0) {
740       // apply audio gen to clip with no audio - prompt for audio settings
741       resaudw = create_resaudw(2, NULL, NULL);
742       /* lives_widget_context_update(); */
743       /* lives_xwindow_raise(lives_widget_get_xwindow(resaudw->dialog)); */
744 
745       if (lives_dialog_run(LIVES_DIALOG(resaudw->dialog)) != LIVES_RESPONSE_OK) return FALSE;
746       if (mainw->error) {
747         mainw->error = FALSE;
748         return FALSE;
749       }
750       has_new_audio = TRUE;
751     }
752     apply_audio_fx = TRUE;
753     if (!apply_rte_audio_init()) return FALSE;
754 
755   }
756 
757   if (type == 1) resize_instance = (weed_plant_t *)rfx->source;
758   else resize_instance = NULL;
759 
760   mainw->internal_messaging = TRUE;
761 
762   mainw->progress_fn = &realfx_progress;
763   mainw->progress_fn(TRUE);
764 
765   weed_reinit_all();
766 
767   retval = do_effect(rfx, FALSE);
768 
769   if (apply_audio_fx) {
770     apply_rte_audio_end(!retval);
771 
772     if (retval) {
773       if (!has_video_filters(FALSE) || !has_video_filters(TRUE)) cfile->undo_action = UNDO_NEW_AUDIO;
774 
775       cfile->undo_achans = cfile->achans;
776       cfile->undo_arate = cfile->arate;
777       cfile->undo_arps = cfile->arps;
778       cfile->undo_asampsize = cfile->asampsize;
779       cfile->undo_signed_endian = cfile->signed_endian;
780 
781     } else {
782       if (has_new_audio) cfile->achans = cfile->asampsize = cfile->arate = cfile->arps = 0;
783       else {
784         char *com = lives_strdup_printf("%s undo_audio %s", prefs->backend_sync, cfile->handle);
785         lives_rm(cfile->info_file);
786         lives_system(com, FALSE);
787         lives_free(com);
788       }
789     }
790     reget_afilesize(mainw->current_file);
791   }
792 
793   mainw->internal_messaging = FALSE;
794   resize_instance = NULL;
795   return retval;
796 }
797 
798 
on_realfx_activate(LiVESMenuItem * menuitem,livespointer xrfx)799 void on_realfx_activate(LiVESMenuItem *menuitem, livespointer xrfx) {
800   lives_rfx_t *rfx = (lives_rfx_t *)xrfx;
801   uint32_t chk_mask = 0;
802   int type = 1;
803 
804   // type can be 0 - apply current realtime effects
805   // 1 - resize (using weed filter) [menuitem == NULL]
806 
807   if (menuitem) {
808     int i;
809     for (i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
810       if (rte_key_valid(i + 1, TRUE)) {
811         if (rte_key_is_enabled(1 + i)) {
812           weed_plant_t *filter = rte_keymode_get_filter(i + 1, rte_key_getmode(i + 1));
813           if (is_pure_audio(filter, TRUE)) {
814             chk_mask |= WARN_MASK_LAYOUT_ALTER_AUDIO;
815           } else chk_mask = WARN_MASK_LAYOUT_ALTER_FRAMES;
816         }
817       }
818     }
819     if (chk_mask > 0) {
820       if (!check_for_layout_errors(NULL, mainw->current_file, cfile->start, cfile->end, &chk_mask)) {
821         return;
822       }
823     }
824     type = 0;
825   }
826 
827   if (!on_realfx_activate_inner(type, rfx)) {
828     unbuffer_lmap_errors(FALSE);
829     return;
830   }
831   if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
832 }
833 
834 
get_blend_layer_inner(weed_timecode_t tc)835 static weed_layer_t *get_blend_layer_inner(weed_timecode_t tc) {
836   static weed_timecode_t blend_tc = 0;
837   lives_clip_t *blend_file;
838   weed_timecode_t ntc = tc;
839 
840   if (!IS_VALID_CLIP(mainw->blend_file)) return NULL;
841   blend_file = mainw->files[mainw->blend_file];
842 
843   if (mainw->blend_file != mainw->last_blend_file) {
844     // mainw->last_blend_file is set to -1 on playback start
845     mainw->last_blend_file = mainw->blend_file;
846     blend_file->last_frameno = blend_file->frameno;
847     blend_tc = tc;
848   }
849 
850   if (!cfile->play_paused) {
851     blend_file->frameno = calc_new_playback_position(mainw->blend_file, blend_tc, (ticks_t *)&ntc);
852     blend_file->last_frameno = blend_file->frameno;
853     blend_tc = ntc;
854   }
855 
856   mainw->blend_layer = lives_layer_new_for_frame(mainw->blend_file, blend_file->frameno);
857   pull_frame_threaded(mainw->blend_layer, get_image_ext_for_type(blend_file->img_type), blend_tc, 0, 0);
858   return mainw->blend_layer;
859 }
860 
get_blend_layer(weed_timecode_t tc)861 void get_blend_layer(weed_timecode_t tc) {
862   /// will set mainw->blend_layer
863   if (mainw->blend_file > -1 && mainw->num_tr_applied > 0
864       && (!mainw->files[mainw->blend_file] ||
865           (mainw->files[mainw->blend_file]->clip_type == CLIP_TYPE_DISK &&
866            (!mainw->files[mainw->blend_file]->frames ||
867             !mainw->files[mainw->blend_file]->is_loaded)))) {
868     // invalid blend file
869     mainw->blend_file = mainw->current_file;
870   }
871 
872   if (mainw->num_tr_applied && mainw->blend_file != mainw->current_file &&
873       IS_VALID_CLIP(mainw->blend_file) && !resize_instance) {
874     get_blend_layer_inner(tc);
875   }
876 }
877 
878 
on_rte_apply(weed_layer_t * layer,int opwidth,int opheight,weed_timecode_t tc)879 weed_plant_t *on_rte_apply(weed_layer_t *layer, int opwidth, int opheight, weed_timecode_t tc) {
880   // apply realtime effects to a layer
881   // mainw->filter_map is used as a guide
882   // mainw->pchains holds the parameter values for interpolation
883   // creates a temporary mix layer from mainw->blend_file (correcting its value if necessary)
884 
885   // returns the effected layer
886 
887   weed_plant_t **layers, *retlayer;
888   int i;
889 
890   if (mainw->foreign) return NULL;
891 
892   layers = (weed_plant_t **)lives_malloc(3 * sizeof(weed_plant_t *));
893   layers[0] = layer;
894   layers[1] = mainw->blend_layer;
895   layers[2] = NULL;
896 
897   if (resize_instance) {
898     lives_filter_error_t ret;
899     weed_plant_t *init_event = weed_plant_new(WEED_PLANT_EVENT);
900     weed_set_int_value(init_event, WEED_LEAF_IN_TRACKS, 0);
901     weed_set_int_value(init_event, WEED_LEAF_OUT_TRACKS, 0);
902 
903     (void)(ret = weed_apply_instance(resize_instance, init_event, layers, 0, 0, tc));
904 
905     retlayer = layers[0];
906     weed_plant_free(init_event);
907   } else {
908     retlayer = weed_apply_effects(layers, mainw->filter_map, tc, opwidth, opheight, mainw->pchains);
909   }
910 
911   // all our pixel_data will have been free'd already
912   for (i = 0; layers[i]; i++) {
913     if (layers[i] != retlayer) weed_layer_free(layers[i]);
914   }
915   lives_free(layers);
916   return retlayer;
917 }
918 
919 
deinterlace_frame(weed_layer_t * layer,weed_timecode_t tc)920 void deinterlace_frame(weed_layer_t *layer, weed_timecode_t tc) {
921   weed_plant_t **layers;
922 
923   weed_plant_t *deint_filter, *deint_instance, *next_inst, *init_event, *orig_instance;
924 
925   int deint_idx, error;
926 
927   if (mainw->fx_candidates[FX_CANDIDATE_DEINTERLACE].delegate == -1) return;
928 
929   deint_idx = LIVES_POINTER_TO_INT(lives_list_nth_data(mainw->fx_candidates[FX_CANDIDATE_DEINTERLACE].list,
930                                    mainw->fx_candidates[FX_CANDIDATE_DEINTERLACE].delegate));
931 
932   deint_filter = get_weed_filter(deint_idx);
933 
934   orig_instance = deint_instance = weed_instance_from_filter(deint_filter);
935 
936   layers = (weed_plant_t **)lives_malloc(2 * sizeof(weed_plant_t *));
937 
938   layers[1] = NULL;
939 
940   layers[0] = layer;
941 
942   init_event = weed_plant_new(WEED_PLANT_EVENT);
943   weed_set_int_value(init_event, WEED_LEAF_IN_TRACKS, 0);
944   weed_set_int_value(init_event, WEED_LEAF_OUT_TRACKS, 0);
945 
946 deint1:
947 
948   weed_apply_instance(deint_instance, init_event, layers, 0, 0, tc);
949 
950   if (weed_plant_has_leaf(deint_instance, WEED_LEAF_HOST_NEXT_INSTANCE)) next_inst = weed_get_plantptr_value(deint_instance,
951         WEED_LEAF_HOST_NEXT_INSTANCE, &error);
952   else next_inst = NULL;
953 
954   weed_call_deinit_func(deint_instance);
955   weed_instance_unref(deint_instance);
956 
957   if (next_inst) {
958     deint_instance = next_inst;
959     goto deint1;
960   }
961 
962   weed_instance_unref(orig_instance);
963 
964   weed_plant_free(init_event);
965 
966   lives_free(layers);
967 }
968 
969 
970 ////////////////////////////////////////////////////////////////////
971 // keypresses
972 // TODO - we should mutex lock mainw->rte
973 
rte_on_off_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)974 boolean rte_on_off_callback(LiVESAccelGroup *group, LiVESWidgetObject *obj, uint32_t keyval, LiVESXModifierType mod,
975                             livespointer user_data) {
976   // this is the callback which happens when a rte is keyed
977   // key is 1 based, but if < 0 then this indicates auto mode (set via data connection)
978   // in automode we don't add the effect parameters in ce_thumbs mode
979   // if non-automode, the user overrides effect toggling
980 
981   int key = LIVES_POINTER_TO_INT(user_data);
982   uint64_t new_rte;
983 
984   if (mainw->go_away) return TRUE;
985   if (!LIVES_IS_INTERACTIVE && group) return TRUE;
986 
987   mainw->fx_is_auto = FALSE;
988 
989   mainw->osc_block = TRUE;
990 
991   if (key < 0) {
992     mainw->fx_is_auto = TRUE;
993     key = -key;
994   }
995 
996   if (key == EFFECT_NONE) {
997     // switch up/down keys to default (fps change)
998     weed_deinit_all(FALSE);
999   } else {
1000     // the idea here is this gets set if a generator starts play, because in weed_init_effect() we will run playback
1001     // and then we come out of there and do not wish to set the key on
1002     key--;
1003     new_rte = GU641 << (key);
1004     mainw->gen_started_play = FALSE;
1005 
1006     if (!(mainw->rte & new_rte)) {
1007       // switch is ON
1008       // WARNING - if we start playing because a generator was started, we block here
1009       filter_mutex_lock(key);
1010       //if (!LIVES_IS_PLAYING) {
1011       if (!(weed_init_effect(key))) {
1012         // ran out of instance slots, no effect assigned, or some other error
1013         // or gen started playback and then stopped
1014         pthread_mutex_lock(&mainw->event_list_mutex);
1015         if (mainw->rte & new_rte) mainw->rte ^= new_rte;
1016         pthread_mutex_unlock(&mainw->event_list_mutex);
1017         if (rte_window) rtew_set_keych(key, FALSE);
1018         if (mainw->ce_thumbs) ce_thumbs_set_keych(key, FALSE);
1019         mainw->osc_block = FALSE;
1020         filter_mutex_unlock(key);
1021         return TRUE;
1022       }
1023 
1024       if (!mainw->gen_started_play) {
1025         pthread_mutex_lock(&mainw->event_list_mutex);
1026         if (!(mainw->rte & new_rte)) mainw->rte |= new_rte;
1027         pthread_mutex_unlock(&mainw->event_list_mutex);
1028 
1029         if (!LIVES_IS_PLAYING) {
1030           // if anything is connected to ACTIVATE, the fx may be activated
1031           // during playback this is checked when we play a frame
1032           for (int i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
1033             if (rte_key_valid(i + 1, TRUE)) {
1034               if (!rte_key_is_enabled(1 + i)) {
1035                 pconx_chain_data(i, rte_key_getmode(i + 1), FALSE);
1036 		// *INDENT-OFF*
1037 	      }}}}
1038 	// *INDENT-ON*
1039 
1040         mainw->last_grabbable_effect = key;
1041         if (rte_window) rtew_set_keych(key, TRUE);
1042         if (mainw->ce_thumbs) {
1043           ce_thumbs_set_keych(key, TRUE);
1044 
1045           // if effect was auto (from ACTIVATE data connection), leave all param boxes
1046           // otherwise, remove any which are not "pinned"
1047           if (!mainw->fx_is_auto) ce_thumbs_add_param_box(key, !mainw->fx_is_auto);
1048         }
1049       }
1050       filter_mutex_unlock(key);
1051     } else {
1052       // effect is OFF
1053       filter_mutex_lock(key);
1054       if (weed_deinit_effect(key)) {
1055         pthread_mutex_lock(&mainw->event_list_mutex);
1056         if (mainw->rte & new_rte) mainw->rte ^= new_rte;
1057         pthread_mutex_unlock(&mainw->event_list_mutex);
1058       }
1059       filter_mutex_unlock(key);
1060       if (!LIVES_IS_PLAYING) {
1061         // if anything is connected to ACTIVATE, the fx may be de-activated
1062         // during playback this is checked when we play a frame
1063         for (int i = 0; i < FX_KEYS_MAX_VIRTUAL; i++) {
1064           if (rte_key_valid(i + 1, TRUE)) {
1065             if (rte_key_is_enabled(1 + i)) {
1066               pconx_chain_data(i, rte_key_getmode(i + 1), FALSE);
1067 	      // *INDENT-OFF*
1068 	    }}}}
1069       // *INDENT-ON*
1070 
1071       if (rte_window) rtew_set_keych(key, FALSE);
1072       if (mainw->ce_thumbs) ce_thumbs_set_keych(key, FALSE);
1073     }
1074   }
1075 
1076   mainw->osc_block = FALSE;
1077 
1078   if (mainw->rendered_fx) {
1079     if (mainw->rendered_fx[0].menuitem && LIVES_IS_WIDGET(mainw->rendered_fx[0].menuitem)) {
1080       if (!LIVES_IS_PLAYING
1081           && mainw->current_file > 0 && ((has_video_filters(FALSE) && !has_video_filters(TRUE))
1082                                          || (cfile->achans > 0 && prefs->audio_src == AUDIO_SRC_INT
1083                                              && has_audio_filters(AF_TYPE_ANY)) || mainw->agen_key != 0)) {
1084         lives_widget_set_sensitive(mainw->rendered_fx[0].menuitem, TRUE);
1085       } else lives_widget_set_sensitive(mainw->rendered_fx[0].menuitem, FALSE);
1086     }
1087   }
1088 
1089   if (key > 0 && !mainw->fx_is_auto) {
1090     // user override any ACTIVATE data connection
1091     override_if_active_input(key);
1092 
1093     // if this is an outlet for ACTIVATE, disable the override now
1094     end_override_if_activate_output(key);
1095   }
1096 
1097   if (LIVES_IS_PLAYING && CURRENT_CLIP_IS_VALID && cfile->play_paused) {
1098     mainw->force_show = TRUE;
1099   }
1100 
1101   mainw->fx_is_auto = FALSE;
1102   return TRUE;
1103 }
1104 
1105 
rte_on_off_callback_hook(LiVESToggleButton * button,livespointer user_data)1106 boolean rte_on_off_callback_hook(LiVESToggleButton * button, livespointer user_data) {
1107   rte_on_off_callback(NULL, NULL, 0, (LiVESXModifierType)0, user_data);
1108   return TRUE;
1109 }
1110 
1111 
grabkeys_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)1112 boolean grabkeys_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
1113                           livespointer user_data) {
1114   // assign the keys to the last key-grabbable effect
1115   int fx = LIVES_POINTER_TO_INT(user_data);
1116   if (fx != -1) {
1117     mainw->last_grabbable_effect = fx;
1118   }
1119   mainw->rte_keys = mainw->last_grabbable_effect;
1120   mainw->osc_block = TRUE;
1121   if (rte_window) {
1122     if (group) rtew_set_keygr(mainw->rte_keys);
1123   }
1124   if (mainw->rte_keys == -1) {
1125     mainw->osc_block = FALSE;
1126     return TRUE;
1127   }
1128   filter_mutex_lock(mainw->rte_keys);
1129   mainw->blend_factor = weed_get_blend_factor(mainw->rte_keys);
1130   filter_mutex_unlock(mainw->rte_keys);
1131   mainw->osc_block = FALSE;
1132   return TRUE;
1133 }
1134 
1135 
textparm_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)1136 boolean textparm_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
1137                           livespointer user_data) {
1138   // keyboard linked to first string parameter, until TAB is pressed
1139   mainw->rte_textparm = get_textparm();
1140   return TRUE;
1141 }
1142 
1143 
grabkeys_callback_hook(LiVESToggleButton * button,livespointer user_data)1144 boolean grabkeys_callback_hook(LiVESToggleButton * button, livespointer user_data) {
1145   if (!lives_toggle_button_get_active(button)) return TRUE;
1146   grabkeys_callback(NULL, NULL, 0, (LiVESXModifierType)0, user_data);
1147   return TRUE;
1148 }
1149 
1150 
rtemode_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)1151 boolean rtemode_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
1152                          livespointer user_data) {
1153   int dirn = LIVES_POINTER_TO_INT(user_data);
1154   // "m" mode key
1155   if (mainw->rte_keys == -1) return TRUE;
1156   rte_key_setmode(0, dirn == PREV_MODE_CYCLE ? -2 : -1);
1157   filter_mutex_lock(mainw->rte_keys);
1158   mainw->blend_factor = weed_get_blend_factor(mainw->rte_keys);
1159   filter_mutex_unlock(mainw->rte_keys);
1160   return TRUE;
1161 }
1162 
1163 
rtemode_callback_hook(LiVESToggleButton * button,livespointer user_data)1164 boolean rtemode_callback_hook(LiVESToggleButton * button, livespointer user_data) {
1165   int key_mode = LIVES_POINTER_TO_INT(user_data);
1166   int modes = rte_getmodespk();
1167   int key = (int)(key_mode / modes);
1168   int mode = key_mode - key * modes;
1169 
1170   if (!lives_toggle_button_get_active(button)) return TRUE;
1171 
1172   rte_key_setmode(key + 1, mode);
1173   return TRUE;
1174 }
1175 
1176 
swap_fg_bg_callback(LiVESAccelGroup * group,LiVESWidgetObject * obj,uint32_t keyval,LiVESXModifierType mod,livespointer user_data)1177 boolean swap_fg_bg_callback(LiVESAccelGroup * group, LiVESWidgetObject * obj, uint32_t keyval, LiVESXModifierType mod,
1178                             livespointer user_data) {
1179   int blend_file = mainw->blend_file;
1180 
1181   if (mainw->playing_file < 1 || mainw->num_tr_applied == 0 || !IS_VALID_CLIP(mainw->blend_file)
1182       || mainw->blend_file == mainw->current_file || mainw->preview || (mainw->is_processing && cfile->is_loaded)) {
1183     return TRUE;
1184   }
1185 
1186   if (mainw->swapped_clip == -1) {
1187     // this is to avoid an annoying situation in VJ playback, where the cliplist postion
1188     // can keep getting reset each time we swap the fg and bg
1189     if (CURRENT_CLIP_IS_NORMAL)
1190       mainw->swapped_clip = mainw->current_file;
1191     else mainw->swapped_clip = mainw->pre_src_file;
1192   } else mainw->swapped_clip = -1;
1193 
1194   //rte_swap_fg_bg();
1195 
1196   mainw->new_clip = blend_file;
1197   //do_quick_switch(blend_file); // will set mainw->blend_file
1198 
1199   if (mainw->ce_thumbs && (mainw->active_sa_clips == SCREEN_AREA_BACKGROUND
1200                            || mainw->active_sa_clips == SCREEN_AREA_FOREGROUND))
1201     ce_thumbs_highlight_current_clip();
1202 
1203   mainw->blend_palette = WEED_PALETTE_END;
1204 
1205   return TRUE;
1206 
1207   // **TODO - for weed, invert all transition parameters for any active effects
1208 }
1209 
1210 //////////////////////////////////////////////////////////////
1211 
1212 
rte_key_is_enabled(int key)1213 LIVES_GLOBAL_INLINE boolean rte_key_is_enabled(int key) {
1214   // key starts at 1
1215   return !((mainw->rte & (GU641 << --key)) == 0ll);
1216 }
1217 
1218 
rte_getmodespk(void)1219 LIVES_GLOBAL_INLINE int rte_getmodespk(void) {
1220   return prefs->max_modes_per_key;
1221 }
1222 
1223 
rte_key_toggle(int key)1224 LIVES_GLOBAL_INLINE boolean rte_key_toggle(int key) {
1225   // key is 1 based, or < 0 for auto mode
1226   rte_on_off_callback(NULL, NULL, 0, (LiVESXModifierType)0, LIVES_INT_TO_POINTER(key));
1227 
1228   return rte_key_is_enabled(key);
1229 }
1230 
1231 
rte_key_on_off(int key,boolean on)1232 boolean rte_key_on_off(int key, boolean on) {
1233   // key is 1 based
1234   // returns the state of the key afterwards
1235   uint64_t new_rte;
1236   if (key < 1 || key >= FX_KEYS_MAX_VIRTUAL) return FALSE;
1237   key--;
1238   new_rte = GU641 << (key);
1239   if (mainw->rte & new_rte) {
1240     if (on) return TRUE;
1241   } else if (!on) return FALSE;
1242   key++;
1243   rte_on_off_callback(NULL, NULL, 0, (LiVESXModifierType)0, LIVES_INT_TO_POINTER(key));
1244   return (mainw->rte & new_rte);
1245 }
1246 
1247 
rte_keys_reset(void)1248 LIVES_GLOBAL_INLINE void rte_keys_reset(void) {
1249   // switch off all realtime effects
1250   rte_on_off_callback(NULL, NULL, 0, (LiVESXModifierType)0, LIVES_INT_TO_POINTER(EFFECT_NONE));
1251 }
1252 
1253 
1254 static int backup_key_modes[FX_KEYS_MAX_VIRTUAL];
1255 static uint64_t backup_rte = 0;
1256 
rte_keymodes_backup(int nkeys)1257 void rte_keymodes_backup(int nkeys) {
1258   // backup the current key/mode state
1259   int i;
1260 
1261   backup_rte = mainw->rte;
1262 
1263   for (i = 0; i < nkeys; i++) {
1264     backup_key_modes[i] = rte_key_getmode(i + 1);
1265   }
1266 }
1267 
1268 
rte_keymodes_restore(int nkeys)1269 void rte_keymodes_restore(int nkeys) {
1270   int i;
1271 
1272   rte_keys_reset();
1273   mainw->rte = backup_rte;
1274 
1275   for (i = 0; i < nkeys; i++) {
1276     // set the mode
1277     rte_key_setmode(i + 1, backup_key_modes[i]);
1278     // activate the key
1279     if ((mainw->rte & GU641 << (i)) != 0) rte_key_toggle(i + 1);
1280   }
1281   if (mainw->rte_keys != -1) {
1282     filter_mutex_lock(mainw->rte_keys);
1283     mainw->blend_factor = weed_get_blend_factor(mainw->rte_keys);
1284     filter_mutex_unlock(mainw->rte_keys);
1285   }
1286 }
1287