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