1 // events.c
2 // LiVES
3 // (c) G. Finch 2005 - 2020 <salsaman+lives@gmail.com>
4 // released under the GNU GPL 3 or later
5 // see file ../COPYING or www.gnu.org for licensing details
6 
7 // functions/structs for event_lists and events
8 
9 #include "main.h"
10 
11 #include "effects.h"
12 #include "interface.h"
13 #include "callbacks.h"
14 #include "resample.h"
15 #include "audio.h"
16 #include "cvirtual.h"
17 #ifdef LIBAV_TRANSCODE
18 #include "transcode.h"
19 #endif
20 
21 //////////////////////////////////
22 //#define DEBUG_EVENTS
23 
24 static int render_choice;
25 static weed_timecode_t last_rec_start_tc = -1;
26 static void **pchains[FX_KEYS_MAX]; // each pchain is an array of void *, these are parameter changes used for rendering
27 
28 ///////////////////////////////////////////////////////
29 
30 //lib stuff
weed_event_get_type(weed_event_t * event)31 LIVES_GLOBAL_INLINE int weed_event_get_type(weed_event_t *event) {
32   if (!event || !WEED_PLANT_IS_EVENT(event)) return WEED_EVENT_TYPE_UNDEFINED;
33   return get_event_type(event);
34 }
35 
weed_frame_event_get_tracks(weed_event_t * event,int ** clips,int64_t ** frames)36 LIVES_GLOBAL_INLINE int weed_frame_event_get_tracks(weed_event_t *event,  int **clips, int64_t **frames) {
37   int ntracks = 0, xntracks = 0;
38   if (!event || !WEED_EVENT_IS_FRAME(event)) return -1;
39   if (clips) *clips = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &ntracks);
40   else ntracks = weed_leaf_num_elements(event, WEED_LEAF_CLIPS);
41   if (frames) *frames = weed_get_int64_array_counted(event, WEED_LEAF_FRAMES, &xntracks);
42   else xntracks = weed_leaf_num_elements(event, WEED_LEAF_FRAMES);
43 
44   if (ntracks != xntracks && xntracks * ntracks > 0) {
45     if (clips) {
46       weed_free(*clips);
47       *clips = NULL;
48     }
49     if (frames) {
50       weed_free(*frames);
51       *frames = NULL;
52     }
53     return -2;
54   }
55   if (ntracks != 0) return ntracks;
56   return xntracks;
57 }
58 
weed_frame_event_get_audio_tracks(weed_event_t * event,int ** clips,double ** seeks)59 LIVES_GLOBAL_INLINE int weed_frame_event_get_audio_tracks(weed_event_t *event,  int **clips, double **seeks) {
60   /// number of actual tracks is actually half of the returned value
61   int ntracks = 0, xntracks = 0;
62   if (!event || !WEED_EVENT_IS_FRAME(event)) return -1;
63   if (clips) *clips = weed_get_int_array_counted(event, WEED_LEAF_AUDIO_CLIPS, &ntracks);
64   else ntracks = weed_leaf_num_elements(event, WEED_LEAF_AUDIO_CLIPS);
65   if (seeks) *seeks = weed_get_double_array_counted(event, WEED_LEAF_AUDIO_SEEKS, &xntracks);
66   else xntracks = weed_leaf_num_elements(event, WEED_LEAF_AUDIO_SEEKS);
67 
68   if (ntracks != xntracks && xntracks * ntracks > 0) {
69     if (clips) {
70       weed_free(*clips);
71       *clips = NULL;
72     }
73     if (seeks) {
74       weed_free(*seeks);
75       *seeks = NULL;
76     }
77     return -2;
78   }
79   if (ntracks != 0) return ntracks;
80   return xntracks;
81 }
82 
weed_event_set_timecode(weed_event_t * event,weed_timecode_t tc)83 LIVES_GLOBAL_INLINE weed_timecode_t weed_event_set_timecode(weed_event_t *event, weed_timecode_t tc) {
84   weed_timecode_t otc = get_event_timecode(event);
85   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
86   return otc;
87 }
88 
weed_event_get_timecode(weed_event_t * event)89 LIVES_GLOBAL_INLINE weed_timecode_t weed_event_get_timecode(weed_event_t *event) {
90   return get_event_timecode(event);
91 }
92 
93 
get_event_pchains(void)94 GNU_PURE void ** *get_event_pchains(void) {return pchains;}
95 
96 #define _get_or_zero(a, b, c) (a ? weed_get_##b##_value(a, c, NULL) : 0)
97 
get_event_timecode(weed_plant_t * plant)98 LIVES_GLOBAL_INLINE weed_timecode_t get_event_timecode(weed_plant_t *plant) {
99   return _get_or_zero(plant, int64, WEED_LEAF_TIMECODE);
100 }
101 
102 
get_event_type(weed_plant_t * plant)103 LIVES_GLOBAL_INLINE int get_event_type(weed_plant_t *plant) {
104   if (!plant) return 0;
105   return weed_get_int_value(plant, WEED_LEAF_EVENT_TYPE, NULL);
106 }
107 
108 
get_prev_event(weed_plant_t * event)109 LIVES_GLOBAL_INLINE weed_plant_t *get_prev_event(weed_plant_t *event) {
110   return _get_or_zero(event, voidptr, WEED_LEAF_PREVIOUS);
111 }
112 
113 
get_next_event(weed_plant_t * event)114 LIVES_GLOBAL_INLINE weed_plant_t *get_next_event(weed_plant_t *event) {
115   return _get_or_zero(event, voidptr, WEED_LEAF_NEXT);
116 }
117 
118 
get_first_event(weed_plant_t * event_list)119 LIVES_GLOBAL_INLINE weed_plant_t *get_first_event(weed_plant_t *event_list) {
120   return _get_or_zero(event_list, voidptr, WEED_LEAF_FIRST);
121 }
122 
123 
get_last_event(weed_plant_t * event_list)124 LIVES_GLOBAL_INLINE weed_plant_t *get_last_event(weed_plant_t *event_list) {
125   return _get_or_zero(event_list, voidptr, WEED_LEAF_LAST);
126 }
127 
128 
has_frame_event_at(weed_plant_t * event_list,weed_timecode_t tc,weed_plant_t ** shortcut)129 boolean has_frame_event_at(weed_plant_t *event_list, weed_timecode_t tc, weed_plant_t **shortcut) {
130   weed_plant_t *event;
131   weed_timecode_t ev_tc;
132 
133   if (!shortcut || !*shortcut) event = get_first_frame_event(event_list);
134   else event = *shortcut;
135 
136   while ((ev_tc = get_event_timecode(event)) <= tc) {
137     if (ev_tc == tc && WEED_EVENT_IS_FRAME(event)) {
138       *shortcut = event;
139       return TRUE;
140     }
141     event = get_next_frame_event(event);
142   }
143   return FALSE;
144 }
145 
146 
get_audio_frame_clip(weed_plant_t * event,int track)147 int get_audio_frame_clip(weed_plant_t *event, int track) {
148   int numaclips, aclipnum = -1;
149   int *aclips;
150   register int i;
151 
152   if (!WEED_EVENT_IS_AUDIO_FRAME(event)) return -2;
153   aclips = weed_get_int_array_counted(event, WEED_LEAF_AUDIO_CLIPS, &numaclips);
154   for (i = 0; i < numaclips; i += 2) {
155     if (aclips[i] == track) {
156       aclipnum = aclips[i + 1];
157       break;
158     }
159   }
160   lives_freep((void **)&aclips);
161   return aclipnum;
162 }
163 
164 
get_audio_frame_vel(weed_plant_t * event,int track)165 double get_audio_frame_vel(weed_plant_t *event, int track) {
166   // vel of 0. is OFF
167   // warning - check for the clip >0 first
168   int *aclips = NULL;
169   double *aseeks = NULL, avel = 1.;
170   int numaclips;
171 
172   if (!WEED_EVENT_IS_AUDIO_FRAME(event)) return -2;
173   aclips = weed_get_int_array_counted(event, WEED_LEAF_AUDIO_CLIPS, &numaclips);
174   aseeks = weed_get_double_array(event, WEED_LEAF_AUDIO_SEEKS, NULL);
175   for (register int i = 0; i < numaclips; i += 2) {
176     if (aclips[i] == track) {
177       avel = aseeks[i + 1];
178       break;
179     }
180   }
181   lives_freep((void **)&aseeks);
182   lives_freep((void **)&aclips);
183   return avel;
184 }
185 
186 
get_audio_frame_seek(weed_plant_t * event,int track)187 double get_audio_frame_seek(weed_plant_t *event, int track) {
188   // warning - check for the clip >0 first
189   int *aclips = NULL;
190   double *aseeks = NULL, aseek = 0.;
191   int numaclips;
192 
193   if (!WEED_EVENT_IS_AUDIO_FRAME(event)) return -1000000.;
194   numaclips = weed_leaf_num_elements(event, WEED_LEAF_AUDIO_CLIPS);
195   aclips = weed_get_int_array_counted(event, WEED_LEAF_AUDIO_CLIPS, &numaclips);
196   aseeks = weed_get_double_array(event, WEED_LEAF_AUDIO_SEEKS, NULL);
197   for (register int i = 0; i < numaclips; i += 2) {
198     if (aclips[i] == track) {
199       aseek = aseeks[i];
200       break;
201     }
202   }
203   lives_freep((void **)&aseeks);
204   lives_freep((void **)&aclips);
205   return aseek;
206 }
207 
208 
get_frame_event_clip(weed_plant_t * event,int layer)209 int get_frame_event_clip(weed_plant_t *event, int layer) {
210   int numclips, clipnum;
211   int *clips;
212   if (!WEED_EVENT_IS_FRAME(event)) return -2;
213   clips = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &numclips);
214   if (numclips <= layer) {
215     lives_freep((void **)&clips);
216     return -3;
217   }
218   clipnum = clips[layer];
219   lives_free(clips);
220   return clipnum;
221 }
222 
223 
get_frame_event_frame(weed_plant_t * event,int layer)224 frames_t get_frame_event_frame(weed_plant_t *event, int layer) {
225   int numframes;
226   frames_t framenum;
227   int64_t *frames;
228   if (!WEED_EVENT_IS_FRAME(event)) return -2;
229   frames = weed_get_int64_array_counted(event, WEED_LEAF_FRAMES, &numframes);
230   if (numframes <= layer) {
231     lives_freep((void **)&frames);
232     return -3;
233   }
234   framenum = (frames_t)frames[layer];
235   lives_free(frames);
236   return framenum;
237 }
238 
239 
lives_event_list_new(weed_event_t * elist,const char * cdate)240 weed_event_t *lives_event_list_new(weed_event_t *elist, const char *cdate) {
241   weed_event_t *evelist;
242   weed_error_t error;
243   char *xdate = (char *)cdate;
244   char *cversion;
245 
246   if (elist) evelist = elist;
247   else {
248     evelist = weed_plant_new(WEED_PLANT_EVENT_LIST);
249     if (!evelist) return NULL;
250     error = weed_set_int_value(evelist, WEED_LEAF_WEED_EVENT_API_VERSION, WEED_EVENT_API_VERSION);
251     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
252     error = weed_set_voidptr_value(evelist, WEED_LEAF_FIRST, NULL);
253     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
254     error = weed_set_voidptr_value(evelist, WEED_LEAF_LAST, NULL);
255     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
256   }
257 
258   if (!weed_plant_has_leaf(evelist, WEED_LEAF_WEED_API_VERSION))
259     error = weed_set_int_value(evelist, WEED_LEAF_WEED_API_VERSION, WEED_API_VERSION);
260   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
261 
262   if (!weed_plant_has_leaf(evelist, WEED_LEAF_FILTER_API_VERSION))
263     error = weed_set_int_value(evelist, WEED_LEAF_FILTER_API_VERSION, WEED_FILTER_API_VERSION);
264   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
265 
266   if (!xdate) {
267     struct timeval otv;
268     gettimeofday(&otv, NULL);
269     xdate = lives_datetime(otv.tv_sec, FALSE);
270   }
271   cversion = lives_strdup_printf("LiVES version %s", LiVES_VERSION);
272 
273   if (!weed_plant_has_leaf(evelist, WEED_LEAF_LIVES_CREATED_VERSION)) {
274     error = weed_set_string_value(evelist, WEED_LEAF_LIVES_CREATED_VERSION, cversion);
275     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
276   }
277   if (!weed_plant_has_leaf(evelist, WEED_LEAF_CREATED_DATE)) {
278     error = weed_set_string_value(evelist, WEED_LEAF_CREATED_DATE, xdate);
279     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
280   }
281 
282   if (!weed_plant_has_leaf(evelist, WEED_LEAF_LIVES_EDITED_VERSION)) {
283     error = weed_set_string_value(evelist, WEED_LEAF_LIVES_EDITED_VERSION, cversion);
284     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
285   }
286   if (!weed_plant_has_leaf(evelist, WEED_LEAF_EDITED_DATE)) {
287     error = weed_set_string_value(evelist, WEED_LEAF_EDITED_DATE, xdate);
288     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
289   }
290 
291   if (xdate != cdate) lives_free(xdate);
292   lives_free(cversion);
293   return evelist;
294 }
295 
296 
unlink_event(weed_plant_t * event_list,weed_plant_t * event)297 void unlink_event(weed_plant_t *event_list, weed_plant_t *event) {
298   // lives_rm event from event_list
299   // don't forget to adjust "timecode" before re-inserting !
300   weed_plant_t *prev_event = get_prev_event(event);
301   weed_plant_t *next_event = get_next_event(event);
302 
303   if (prev_event) weed_set_voidptr_value(prev_event, WEED_LEAF_NEXT, next_event);
304   if (next_event) weed_set_voidptr_value(next_event, WEED_LEAF_PREVIOUS, prev_event);
305 
306   if (get_first_event(event_list) == event) weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, next_event);
307   if (get_last_event(event_list) == event) weed_set_voidptr_value(event_list, WEED_LEAF_LAST, prev_event);
308 }
309 
310 
delete_event(weed_plant_t * event_list,weed_plant_t * event)311 void delete_event(weed_plant_t *event_list, weed_plant_t *event) {
312   // delete event from event_list
313   threaded_dialog_spin(0.);
314   unlink_event(event_list, event);
315   if (mainw->multitrack) mt_fixup_events(mainw->multitrack, event, NULL);
316   weed_plant_free(event);
317   threaded_dialog_spin(0.);
318 }
319 
320 
insert_event_before(weed_plant_t * at_event,weed_plant_t * event)321 boolean insert_event_before(weed_plant_t *at_event, weed_plant_t *event) {
322   // insert event before at_event : returns FALSE if event is new start of event list
323   weed_plant_t *xevent = get_prev_event(at_event);
324   if (xevent) weed_set_voidptr_value(xevent, WEED_LEAF_NEXT, event);
325   weed_set_voidptr_value(event, WEED_LEAF_NEXT, at_event);
326   weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, xevent);
327   weed_set_voidptr_value(at_event, WEED_LEAF_PREVIOUS, event);
328   if (get_event_timecode(event) > get_event_timecode(at_event))
329     lives_printerr("Warning ! Inserted out of order event type %d before %d\n", get_event_type(event), get_event_type(at_event));
330   return (xevent != NULL);
331 }
332 
333 
insert_event_after(weed_plant_t * at_event,weed_plant_t * event)334 boolean insert_event_after(weed_plant_t *at_event, weed_plant_t *event) {
335   // insert event after at_event : returns FALSE if event is new end of event list
336   weed_plant_t *xevent = get_next_event(at_event);
337   if (xevent) weed_set_voidptr_value(xevent, WEED_LEAF_PREVIOUS, event);
338   weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, at_event);
339   weed_set_voidptr_value(event, WEED_LEAF_NEXT, xevent);
340   weed_set_voidptr_value(at_event, WEED_LEAF_NEXT, event);
341   if (get_event_timecode(event) < get_event_timecode(at_event))
342     lives_printerr("Warning ! Inserted out of order event type %d after %d\n", get_event_type(event), get_event_type(at_event));
343   return (xevent != NULL);
344 }
345 
346 
replace_event(weed_plant_t * event_list,weed_plant_t * at_event,weed_plant_t * event)347 void replace_event(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event) {
348   // replace at_event with event; free at_event
349   if (mainw->multitrack) mt_fixup_events(mainw->multitrack, at_event, event);
350   weed_set_int64_value(event, WEED_LEAF_TIMECODE, get_event_timecode(at_event));
351   if (!insert_event_after(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
352   delete_event(event_list, at_event);
353 }
354 
355 
get_next_frame_event(weed_plant_t * event)356 weed_plant_t *get_next_frame_event(weed_plant_t *event) {
357   weed_plant_t *next;
358   if (!event) return NULL;
359   next = get_next_event(event);
360   while (next) {
361     if (WEED_EVENT_IS_FRAME(next)) return next;
362     next = get_next_event(next);
363   }
364   return NULL;
365 }
366 
367 
get_prev_frame_event(weed_plant_t * event)368 weed_plant_t *get_prev_frame_event(weed_plant_t *event) {
369   weed_plant_t *prev;
370   if (!event) return NULL;
371   prev = get_prev_event(event);
372   while (prev) {
373     if (WEED_EVENT_IS_FRAME(prev)) return prev;
374     prev = get_prev_event(prev);
375   }
376   return NULL;
377 }
378 
379 
get_next_audio_frame_event(weed_plant_t * event)380 weed_plant_t *get_next_audio_frame_event(weed_plant_t *event) {
381   weed_plant_t *next;
382   if (!event) return NULL;
383   next = get_next_event(event);
384   while (next) {
385     if (WEED_EVENT_IS_AUDIO_FRAME(next)) return next;
386     next = get_next_event(next);
387   }
388   return NULL;
389 }
390 
391 
get_prev_audio_frame_event(weed_plant_t * event)392 weed_plant_t *get_prev_audio_frame_event(weed_plant_t *event) {
393   weed_plant_t *prev;
394   if (!event) return NULL;
395   prev = get_prev_event(event);
396   while (prev) {
397     if (WEED_EVENT_IS_AUDIO_FRAME(prev)) return prev;
398     prev = get_prev_event(prev);
399   }
400   return NULL;
401 }
402 
403 
get_first_frame_event(weed_plant_t * event_list)404 weed_plant_t *get_first_frame_event(weed_plant_t *event_list) {
405   weed_plant_t *event;
406 
407   if (!event_list) return NULL;
408 
409   event = get_first_event(event_list);
410 
411   while (event) {
412     if (WEED_EVENT_IS_FRAME(event)) return event;
413     event = get_next_event(event);
414   }
415   return NULL;
416 }
417 
418 
get_last_frame_event(weed_plant_t * event_list)419 weed_plant_t *get_last_frame_event(weed_plant_t *event_list) {
420   weed_plant_t *event;
421 
422   if (!event_list) return NULL;
423 
424   event = get_last_event(event_list);
425 
426   while (event) {
427     if (WEED_EVENT_IS_FRAME(event)) return event;
428     event = get_prev_event(event);
429   }
430   return NULL;
431 }
432 
433 
get_audio_block_start(weed_plant_t * event_list,int track,weed_timecode_t tc,boolean seek_back)434 weed_plant_t *get_audio_block_start(weed_plant_t *event_list, int track, weed_timecode_t tc, boolean seek_back) {
435   // find any event which starts an audio block on track at timecode tc
436   // if seek_back is true we go back in time to find a possible start
437   // otherwise just check the current frame event
438 
439   weed_plant_t *event = get_frame_event_at_or_before(event_list, tc, NULL);
440   if (get_audio_frame_clip(event, track) > -1 && get_audio_frame_vel(event, track) != 0.) return event;
441   if (!seek_back) return NULL;
442 
443   while ((event = get_prev_frame_event(event)) != NULL) {
444     if (get_audio_frame_clip(event, track) > -1 && get_audio_frame_vel(event, track) != 0.) return event;
445   }
446 
447   return NULL;
448 }
449 
450 static LiVESList *trans_list = NULL;
451 
452 typedef struct {
453   weed_event_t *in_event;
454   weed_event_t *out_event;
455 } trans_entry;
456 
add_init_to_ttable(weed_event_t * in_event,weed_event_t * out_event)457 static void add_init_to_ttable(weed_event_t *in_event, weed_event_t *out_event) {
458   trans_entry *tr_entry = (trans_entry *)lives_malloc(sizeof(trans_entry));
459   tr_entry->in_event = in_event;
460   tr_entry->out_event = out_event;
461   trans_list = lives_list_prepend(trans_list, tr_entry);
462 }
463 
find_init_event_by_id(weed_plant_t * event,boolean remove)464 static weed_event_t *find_init_event_by_id(weed_plant_t *event, boolean remove) {
465   LiVESList *list = trans_list;
466   for (; list; list = list->next) {
467     trans_entry *tr_entry = (trans_entry *)list->data;
468     if (tr_entry->in_event == event) {
469       if (!remove) return tr_entry->out_event;
470       else {
471         weed_event_t *out_event = tr_entry->out_event;
472         if (list->prev) list->prev->next = list->next;
473         else trans_list = list->next;
474         if (list->next) list->next->prev = list->prev;
475         list->prev = list->next = NULL;
476         lives_free(list->data);
477         list->data = NULL;
478         lives_list_free(list);
479         return out_event;
480       }
481     }
482   }
483   return NULL;
484 }
485 
reset_ttable(void)486 void reset_ttable(void) {lives_list_free_all(&trans_list);}
487 
488 
remove_frame_from_event(weed_plant_t * event_list,weed_plant_t * event,int track)489 void remove_frame_from_event(weed_plant_t *event_list, weed_plant_t *event, int track) {
490   // TODO - memcheck
491   weed_timecode_t tc;
492 
493   int *clips;
494   int64_t *frames;
495 
496   int numframes;
497   register int i;
498 
499   if (!WEED_EVENT_IS_FRAME(event)) return;
500 
501   tc = get_event_timecode(event);
502 
503   clips = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &numframes);
504   frames = weed_get_int64_array(event, WEED_LEAF_FRAMES, NULL);
505 
506   if (track == numframes - 1) numframes--;
507   else {
508     clips[track] = -1;
509     frames[track] = 0;
510   }
511 
512   // if stack is empty, we will replace with a blank frame
513   for (i = 0; i < numframes && clips[i] < 1; i++);
514   if (i == numframes) {
515     if (event == get_last_event(event_list) && !WEED_EVENT_IS_AUDIO_FRAME(event)) delete_event(event_list, event);
516     else event_list = insert_blank_frame_event_at(event_list, tc, &event);
517   } else event_list = insert_frame_event_at(event_list, tc, numframes, clips, frames, &event);
518   lives_free(frames);
519   lives_free(clips);
520 }
521 
522 
is_blank_frame(weed_plant_t * event,boolean count_audio)523 boolean is_blank_frame(weed_plant_t *event, boolean count_audio) {
524   int clip, numframes;
525   int64_t frame;
526 
527   if (!WEED_EVENT_IS_FRAME(event)) return FALSE;
528   if (count_audio && WEED_EVENT_IS_AUDIO_FRAME(event)) {
529     int *aclips = weed_get_int_array(event, WEED_LEAF_AUDIO_CLIPS, NULL);
530     if (aclips[1] > 0) {
531       lives_free(aclips);
532       return FALSE;   // has audio seek
533     }
534     lives_free(aclips);
535   }
536   numframes = weed_leaf_num_elements(event, WEED_LEAF_CLIPS);
537   if (numframes > 1) return FALSE;
538   clip = weed_get_int_value(event, WEED_LEAF_CLIPS, NULL);
539   frame = weed_get_int64_value(event, WEED_LEAF_FRAMES, NULL);
540 
541   if (clip < 0 || frame <= 0) return TRUE;
542   return FALSE;
543 }
544 
545 
remove_end_blank_frames(weed_plant_t * event_list,boolean remove_filter_inits)546 void remove_end_blank_frames(weed_plant_t *event_list, boolean remove_filter_inits) {
547   // remove blank frames from end of event list
548   weed_plant_t *event = get_last_event(event_list), *prevevent;
549   while (event) {
550     prevevent = get_prev_event(event);
551     if (!WEED_EVENT_IS_FRAME(event) && !WEED_EVENT_IS_FILTER_INIT(event)) {
552       event = prevevent;
553       continue;
554     }
555     if (remove_filter_inits && WEED_EVENT_IS_FILTER_INIT(event)) remove_filter_from_event_list(event_list, event);
556     else {
557       if (!is_blank_frame(event, TRUE)) break;
558       delete_event(event_list, event);
559     }
560     event = prevevent;
561   }
562 }
563 
564 
get_next_paramchange(void ** pchange_next,weed_timecode_t end_tc)565 weed_timecode_t get_next_paramchange(void **pchange_next, weed_timecode_t end_tc) {
566   weed_timecode_t min_tc = end_tc;
567   register int i = 0;
568   if (!pchange_next) return end_tc;
569   for (; pchange_next[i]; i++) if (get_event_timecode((weed_plant_t *)pchange_next[i]) < min_tc)
570       min_tc = get_event_timecode((weed_plant_t *)pchange_next[i]);
571   return min_tc;
572 }
573 
574 
get_prev_paramchange(void ** pchange_prev,weed_timecode_t start_tc)575 weed_timecode_t get_prev_paramchange(void **pchange_prev, weed_timecode_t start_tc) {
576   weed_timecode_t min_tc = start_tc;
577   register int i = 0;
578   if (!pchange_prev) return start_tc;
579   for (; pchange_prev[i]; i++) if (get_event_timecode((weed_plant_t *)pchange_prev[i]) < min_tc)
580       min_tc = get_event_timecode((weed_plant_t *)pchange_prev[i]);
581   return min_tc;
582 }
583 
584 
is_init_pchange(weed_plant_t * init_event,weed_plant_t * pchange_event)585 boolean is_init_pchange(weed_plant_t *init_event, weed_plant_t *pchange_event) {
586   // a PARAM_CHANGE is an init_pchange iff both events have the same tc, and there is no frame event between the two events
587   // normally we could check the "in_params" of the init_event for a match, but here we may be rebuilding the event list
588   // so the values will not confer
589   weed_plant_t *event = init_event;
590   weed_timecode_t tc = get_event_timecode(event);
591   if (tc != get_event_timecode(pchange_event)) return FALSE;
592 
593   while (event && event != pchange_event) {
594     if (WEED_EVENT_IS_FRAME(event)) return FALSE;
595     event = get_next_event(event);
596   }
597   return TRUE;
598 }
599 
600 
601 /**
602    @brief copy (duplicate) in_event and append it to event_list, changing the timecode to out_tc
603    this is called during quantisation
604 
605    copy an event and insert it in event_list
606    events must be copied in time order, since filter_deinit,
607    filter_map and param_change events MUST refer to prior filter_init events
608 
609    when we copy a filter_init, we add a new field to the copy "event_id".
610    This contains the value of the pointer to original event
611    we use this to locate the effect_init event in the new event_list
612 
613    for effect_deinit, effect_map, param_change, we change the "init_event(s)" property to point to our copy effect_init
614 
615    we don't need to make pchain array here, provided we later call event_list_rectify()
616    (this only applies to multitrack, since we interpolate parameters there; in clip editor the paramter changes are applied
617    as recorded, with no interpolation)
618 
619    we check for memory allocation errors here, because we could be building a large new event_list
620    on mem error we return NULL, caller should free() event_list in that case
621 */
event_copy_and_insert(weed_plant_t * in_event,weed_timecode_t out_tc,weed_plant_t * event_list,weed_event_t ** ret_event)622 weed_plant_t *event_copy_and_insert(weed_plant_t *in_event, weed_timecode_t out_tc, weed_plant_t *event_list,
623                                     weed_event_t **ret_event) {
624   void **in_pchanges;
625 
626   weed_plant_t *event;
627   weed_plant_t *event_after = NULL;
628   weed_plant_t *event_before = NULL;
629   weed_plant_t *filter;
630 
631   void *init_event, *new_init_event, **init_events;
632   char *filter_hash;
633 
634   weed_error_t error;
635 
636   int etype;
637   int num_events;
638   int idx, num_params;
639 
640   int i;
641 
642   if (!in_event) return event_list;
643 
644   if (!event_list) {
645     event_list = lives_event_list_new(NULL, NULL);
646     if (!event_list) return NULL;
647     event_before = NULL;
648   } else {
649     event_before = get_last_event(event_list);
650     while (event_before) {
651       if (get_event_timecode(event_before) < out_tc || (get_event_timecode(event_before) == out_tc
652           && (!WEED_EVENT_IS_FRAME(event_before) ||
653               WEED_EVENT_IS_FILTER_DEINIT(in_event)))) break;
654       event_before = get_prev_event(event_before);
655     }
656   }
657 
658   event = weed_plant_copy(in_event);
659   weed_event_set_timecode(event, out_tc);
660 
661   // need to repoint our avol_init_event
662   if (mainw->multitrack) mt_fixup_events(mainw->multitrack, in_event, event);
663   if (!event) return NULL;
664 
665   if (!event_before) {
666     event_after = get_first_event(event_list);
667     error = weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
668     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
669     if (event_after == event) event_after = NULL;
670   } else {
671     event_after = get_next_event(event_before);
672     error = weed_set_voidptr_value(event_before, WEED_LEAF_NEXT, event);
673     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
674   }
675 
676   error = weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, event_before);
677   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
678 
679   error = weed_set_voidptr_value(event, WEED_LEAF_NEXT, event_after);
680   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
681 
682   if (!event_after) error = weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
683   else error = weed_set_voidptr_value(event_after, WEED_LEAF_PREVIOUS, event);
684   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
685 
686   etype = get_event_type(in_event);
687   switch (etype) {
688   case WEED_EVENT_TYPE_FILTER_INIT:
689     /* weed_leaf_delete(event, WEED_LEAF_EVENT_ID); */
690     /* error = weed_set_voidptr_value(event, WEED_LEAF_EVENT_ID, (void *)in_event); */
691     /* if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL; */
692     add_init_to_ttable(in_event, event);
693     filter_hash = weed_get_string_value(event, WEED_LEAF_FILTER, &error);
694     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
695     if ((idx = weed_get_idx_for_hashname(filter_hash, TRUE)) != -1) {
696       filter = get_weed_filter(idx);
697       if ((num_params = num_in_params(filter, FALSE, FALSE)) > 0) {
698         in_pchanges = (void **)lives_malloc((num_params + 1) * sizeof(void *));
699         if (!in_pchanges) return NULL;
700         for (i = 0; i < num_params; i++) in_pchanges[i] = NULL;
701         error = weed_set_voidptr_array(event, WEED_LEAF_IN_PARAMETERS, num_params,
702                                        in_pchanges); // set all to NULL, we will re-fill as we go along
703         lives_free(in_pchanges);
704         if (error == WEED_ERROR_MEMORY_ALLOCATION) {
705           lives_free(filter_hash);
706           return NULL;
707         }
708       }
709       lives_free(filter_hash);
710     }
711     break;
712   case WEED_EVENT_TYPE_FILTER_DEINIT:
713     init_event = weed_get_voidptr_value(in_event, WEED_LEAF_INIT_EVENT, NULL);
714     new_init_event = find_init_event_by_id(init_event, TRUE);
715     error = weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENT, new_init_event);
716     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
717     /* weed_leaf_delete((weed_plant_t *)new_init_event, WEED_LEAF_EVENT_ID); */
718     /* error = weed_set_voidptr_value((weed_plant_t *)new_init_event, WEED_LEAF_EVENT_ID, */
719     /*                                (void *)new_init_event);  // useful later for event_list_rectify */
720     weed_leaf_delete((weed_plant_t *)new_init_event,
721                      WEED_LEAF_DEINIT_EVENT); // delete since we assign a placeholder with int64 type
722     weed_set_plantptr_value((weed_plant_t *)new_init_event, WEED_LEAF_DEINIT_EVENT, event);
723     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
724     break;
725   case WEED_EVENT_TYPE_FILTER_MAP:
726     // set WEED_LEAF_INIT_EVENTS property
727     init_events = weed_get_voidptr_array_counted(in_event, WEED_LEAF_INIT_EVENTS, &num_events);
728     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
729     for (i = 0; i < num_events; i++) {
730       init_events[i] = find_init_event_by_id(init_events[i], FALSE);
731     }
732     error = weed_set_voidptr_array(event, WEED_LEAF_INIT_EVENTS, num_events, init_events);
733     lives_free(init_events);
734     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
735     /*
736       // remove any prior FILTER_MAPs at the same timecode
737       event_before = get_prev_event(event);
738       while (event_before) {
739       weed_plant_t *event_before_event_before = get_prev_event(event_before);
740       weed_timecode_t tc = get_event_timecode(event_before);
741       if (tc < out_tc) break;
742       if (tc == out_tc && (WEED_EVENT_IS_FILTER_MAP(event_before))) delete_event(event_list, event_before);
743       event_before = event_before_event_before;
744       }*/
745     break;
746   case WEED_EVENT_TYPE_PARAM_CHANGE:
747     init_event = weed_get_voidptr_value(in_event, WEED_LEAF_INIT_EVENT, &error);
748     new_init_event = find_init_event_by_id(init_event, FALSE);
749     error = weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENT, new_init_event);
750     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
751     weed_set_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, NULL);
752     weed_set_voidptr_value(event, WEED_LEAF_PREV_CHANGE, NULL);
753     break;
754   }
755 
756   if (ret_event) *ret_event = event;
757   return event_list;
758 }
759 
760 
frame_event_has_frame_for_track(weed_plant_t * event,int track)761 boolean frame_event_has_frame_for_track(weed_plant_t *event, int track) {
762   int *clips, numclips;
763   int64_t *frames;
764 
765   clips = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &numclips);
766   if (numclips <= track) return FALSE;
767   frames = weed_get_int64_array(event, WEED_LEAF_FRAMES, NULL);
768 
769   if (clips[track] > 0 && frames[track] > 0) {
770     lives_free(clips);
771     lives_free(frames);
772     return TRUE;
773   }
774   lives_free(clips);
775   lives_free(frames);
776   return FALSE;
777 }
778 
779 
get_frame_event_at(weed_plant_t * event_list,weed_timecode_t tc,weed_plant_t * shortcut,boolean exact)780 weed_plant_t *get_frame_event_at(weed_plant_t *event_list, weed_timecode_t tc, weed_plant_t *shortcut, boolean exact) {
781   // if exact is FALSE, we can get a frame event just after tc
782   weed_plant_t *event, *next_event;
783   weed_timecode_t xtc, next_tc = 0;
784 
785   if (!event_list) return NULL;
786   if (shortcut) event = shortcut;
787   else event = get_first_frame_event(event_list);
788   while (event) {
789     next_event = get_next_event(event);
790     if (next_event) next_tc = get_event_timecode(next_event);
791     xtc = get_event_timecode(event);
792     if ((labs(tc - xtc) <= 10 || ((next_tc > tc || !next_event) && !exact)) &&
793         WEED_EVENT_IS_FRAME(event)) {
794       return event;
795     }
796     if (xtc > tc) return NULL;
797     event = next_event;
798   }
799   return NULL;
800 }
801 
802 
filter_map_after_frame(weed_plant_t * fmap)803 boolean filter_map_after_frame(weed_plant_t *fmap) {
804   // return TRUE if filter_map follows frame at same timecode
805   weed_plant_t *frame = get_prev_frame_event(fmap);
806 
807   if (frame && get_event_timecode(frame) == get_event_timecode(fmap)) return TRUE;
808   return FALSE;
809 }
810 
811 
get_frame_event_at_or_before(weed_plant_t * event_list,weed_timecode_t tc,weed_plant_t * shortcut)812 weed_plant_t *get_frame_event_at_or_before(weed_plant_t *event_list, weed_timecode_t tc, weed_plant_t *shortcut) {
813   weed_plant_t *frame_event = get_frame_event_at(event_list, tc, shortcut, FALSE);
814   while (frame_event && get_event_timecode(frame_event) > tc) {
815     frame_event = get_prev_frame_event(frame_event);
816   }
817   return frame_event;
818 }
819 
820 
get_filter_map_after(weed_plant_t * event,int ctrack)821 weed_plant_t *get_filter_map_after(weed_plant_t *event, int ctrack) {
822   // get filter_map following event; if ctrack!=LIVES_TRACK_ANY then we ignore filter maps with no in_track/out_track == ctrack
823   void **init_events;
824   weed_plant_t *init_event;
825   int num_init_events;
826 
827   register int i;
828 
829   while (event) {
830     if (WEED_EVENT_IS_FILTER_MAP(event)) {
831       if (ctrack == LIVES_TRACK_ANY) return event;
832       if (!weed_plant_has_leaf(event, WEED_LEAF_INIT_EVENTS)) {
833         event = get_next_event(event);
834         continue;
835       }
836       init_events = weed_get_voidptr_array_counted(event, WEED_LEAF_INIT_EVENTS, &num_init_events);
837       if (!init_events[0]) {
838         lives_free(init_events);
839         event = get_next_event(event);
840         continue;
841       }
842       for (i = 0; i < num_init_events; i++) {
843         init_event = (weed_plant_t *)init_events[i];
844 
845         if (init_event_is_relevant(init_event, ctrack)) {
846           lives_free(init_events);
847           return event;
848         }
849 
850       }
851       lives_freep((void **)&init_events);
852     }
853     event = get_next_event(event);
854   }
855   return NULL;
856 }
857 
858 
init_event_is_relevant(weed_plant_t * init_event,int ctrack)859 boolean init_event_is_relevant(weed_plant_t *init_event, int ctrack) {
860   // see if init_event mentions ctrack as an in_track or an out_track
861 
862   int *in_tracks, *out_tracks;
863   int num_tracks;
864 
865   register int j;
866 
867   //if (init_event_is_process_last(init_event)) return FALSE;
868 
869   if (weed_plant_has_leaf(init_event, WEED_LEAF_IN_TRACKS)) {
870     in_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_IN_TRACKS, &num_tracks);
871     for (j = 0; j < num_tracks; j++) {
872       if (in_tracks[j] == ctrack) {
873         lives_free(in_tracks);
874         return TRUE;
875       }
876     }
877     lives_freep((void **)&in_tracks);
878   }
879 
880   if (weed_plant_has_leaf(init_event, WEED_LEAF_OUT_TRACKS)) {
881     out_tracks = weed_get_int_array_counted(init_event, WEED_LEAF_OUT_TRACKS, &num_tracks);
882     for (j = 0; j < num_tracks; j++) {
883       if (out_tracks[j] == ctrack) {
884         lives_free(out_tracks);
885         return TRUE;
886       }
887     }
888     lives_freep((void **)&out_tracks);
889   }
890 
891   return FALSE;
892 }
893 
894 
get_filter_map_before(weed_plant_t * event,int ctrack,weed_plant_t * stop_event)895 weed_plant_t *get_filter_map_before(weed_plant_t *event, int ctrack, weed_plant_t *stop_event) {
896   // get filter_map preceding event; if ctrack!=LIVES_TRACK_ANY then we ignore
897   // filter maps with no in_track/out_track == ctrack
898 
899   // we will stop searching when we reach stop_event; if it is NULL we will search back to
900   // start of event list
901 
902   void **init_events;
903   weed_plant_t *init_event;
904   int num_init_events;
905 
906   register int i;
907 
908   while (event != stop_event && event) {
909     if (WEED_EVENT_IS_FILTER_MAP(event)) {
910       if (ctrack == LIVES_TRACK_ANY) return event;
911       if (!weed_plant_has_leaf(event, WEED_LEAF_INIT_EVENTS)) {
912         event = get_prev_event(event);
913         continue;
914       }
915       init_events = weed_get_voidptr_array_counted(event, WEED_LEAF_INIT_EVENTS, &num_init_events);
916       if (!init_events[0]) {
917         lives_free(init_events);
918         event = get_prev_event(event);
919         continue;
920       }
921       for (i = 0; i < num_init_events; i++) {
922         init_event = (weed_plant_t *)init_events[i];
923         if (init_event_is_relevant(init_event, ctrack)) {
924           lives_free(init_events);
925           return event;
926         }
927       }
928       lives_freep((void **)&init_events);
929     }
930     event = get_prev_event(event);
931   }
932   return event;
933 }
934 
935 
get_init_events_before(weed_plant_t * event,weed_plant_t * init_event,boolean add)936 void **get_init_events_before(weed_plant_t *event, weed_plant_t *init_event, boolean add) {
937   // find previous FILTER_MAP event, and append or delete new init_event
938   void **init_events = NULL, **new_init_events;
939   int error, num_init_events = 0;
940   register int i, j = 0;
941 
942   while (event) {
943     if (WEED_EVENT_IS_FILTER_MAP(event)) {
944       if ((init_events = weed_get_voidptr_array_counted(event, WEED_LEAF_INIT_EVENTS, &num_init_events)) != NULL) {
945         if (add) new_init_events = (void **)lives_malloc((num_init_events + 2) * sizeof(void *));
946         else new_init_events = (void **)lives_malloc((num_init_events + 1) * sizeof(void *));
947 
948         for (i = 0; i < num_init_events; i++) if ((add || (init_event && (init_events[i] != (void *)init_event))) &&
949               init_events[0]) {
950             new_init_events[j++] = init_events[i];
951             if (add && init_events[i] == (void *)init_event) add = FALSE; // don't add twice
952           }
953 
954         if (add) {
955           char *fhash;
956           weed_plant_t *filter;
957           int k, l, tflags;
958           // add before any "process_last" events
959           k = j;
960           while (k > 0) {
961             k--;
962             if (mainw->multitrack && init_events[k] == mainw->multitrack->avol_init_event) {
963               // add before the audio mixer
964               continue;
965             }
966             fhash = weed_get_string_value((weed_plant_t *)init_events[k], WEED_LEAF_FILTER, &error);
967             filter = get_weed_filter(weed_get_idx_for_hashname(fhash, TRUE));
968             lives_free(fhash);
969             if (weed_plant_has_leaf(filter, WEED_LEAF_FLAGS)) {
970               tflags = weed_get_int_value(filter, WEED_LEAF_FLAGS, &error);
971               if (tflags & WEED_FILTER_HINT_PROCESS_LAST) {
972                 // add before any "process_last" filters
973                 continue;
974               }
975             }
976             k++;
977             break;
978           }
979           // insert new event at slot k
980           // make gap for new filter
981           for (l = j - 1; l >= k; l--) {
982             new_init_events[l + 1] = new_init_events[l];
983           }
984           new_init_events[k] = (void *)init_event;
985           j++;
986         }
987 
988         new_init_events[j] = NULL;
989         if (init_events) lives_free(init_events);
990         return new_init_events;
991       }
992       if (init_events) lives_free(init_events);
993     }
994     event = get_prev_event(event);
995   }
996   // no previous init_events found
997   if (add) {
998     new_init_events = (void **)lives_malloc(2 * sizeof(void *));
999     new_init_events[0] = (void *)init_event;
1000     new_init_events[1] = NULL;
1001   } else {
1002     new_init_events = (void **)lives_malloc(sizeof(void *));
1003     new_init_events[0] = NULL;
1004   }
1005   return new_init_events;
1006 }
1007 
1008 
update_filter_maps(weed_plant_t * event,weed_plant_t * end_event,weed_plant_t * init_event)1009 void update_filter_maps(weed_plant_t *event, weed_plant_t *end_event, weed_plant_t *init_event) {
1010   // append init_event to all FILTER_MAPS between event and end_event
1011 
1012   while (event != end_event) {
1013     if (WEED_EVENT_IS_FILTER_MAP(event)) {
1014       add_init_event_to_filter_map(event, init_event, NULL);
1015     }
1016     event = get_next_event(event);
1017   }
1018 }
1019 
1020 
insert_filter_init_event_at(weed_plant_t * event_list,weed_plant_t * at_event,weed_plant_t * event)1021 void insert_filter_init_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event) {
1022   // insert event as first event at same timecode as (FRAME_EVENT) at_event
1023   weed_timecode_t tc = get_event_timecode(at_event);
1024   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
1025 
1026   while (at_event) {
1027     at_event = get_prev_event(at_event);
1028     if (!at_event) break;
1029     if (get_event_timecode(at_event) < tc) {
1030       if (!insert_event_after(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
1031 
1032       return;
1033     }
1034   }
1035 
1036   // event is first
1037   at_event = get_first_event(event_list);
1038   insert_event_before(at_event, event);
1039   weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
1040 }
1041 
1042 
insert_filter_deinit_event_at(weed_plant_t * event_list,weed_plant_t * at_event,weed_plant_t * event)1043 void insert_filter_deinit_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event) {
1044   // insert event as last at same timecode as (FRAME_EVENT) at_event
1045   weed_timecode_t tc = get_event_timecode(at_event);
1046   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
1047 
1048   while (at_event) {
1049     if (WEED_EVENT_IS_FRAME(at_event)) {
1050       if (!insert_event_after(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
1051       return;
1052     }
1053     if (get_event_timecode(at_event) > tc) {
1054       if (!insert_event_before(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
1055       return;
1056     }
1057     at_event = get_next_event(at_event);
1058   }
1059   // event is last
1060   at_event = get_last_event(event_list);
1061   insert_event_after(at_event, event);
1062   weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
1063 }
1064 
1065 
insert_filter_map_event_at(weed_plant_t * event_list,weed_plant_t * at_event,weed_plant_t * event,boolean before_frames)1066 boolean insert_filter_map_event_at(weed_plant_t *event_list, weed_plant_t *at_event,
1067                                    weed_plant_t *event, boolean before_frames) {
1068   // insert event as last event at same timecode as (FRAME_EVENT) at_event
1069   weed_timecode_t tc = get_event_timecode(at_event);
1070   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
1071 
1072   if (before_frames) {
1073     while (at_event) {
1074       at_event = get_prev_event(at_event);
1075       if (!at_event) break;
1076       if (WEED_EVENT_IS_FILTER_MAP(at_event)) {
1077         // found an existing FILTER_MAP, we can simply replace it
1078         if (mainw->filter_map == at_event) mainw->filter_map = event;
1079         replace_event(event_list, at_event, event);
1080         return TRUE;
1081       }
1082       if (WEED_EVENT_IS_FILTER_INIT(at_event) || get_event_timecode(at_event) < tc) {
1083         if (!insert_event_after(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
1084         return TRUE;
1085       }
1086     }
1087     // event is first
1088     at_event = get_first_event(event_list);
1089     insert_event_before(at_event, event);
1090     weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
1091   } else {
1092     // insert after frame events
1093     while (at_event) {
1094       at_event = get_next_event(at_event);
1095       if (!at_event) break;
1096       if (WEED_EVENT_IS_FILTER_MAP(at_event)) {
1097         // found an existing FILTER_MAP, we can simply replace it
1098         if (mainw->filter_map == at_event) mainw->filter_map = event;
1099         replace_event(event_list, at_event, event);
1100         return TRUE;
1101       }
1102       if (get_event_timecode(at_event) > tc) {
1103         if (!insert_event_before(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
1104         return TRUE;
1105       }
1106     }
1107     // event is last
1108     at_event = get_last_event(event_list);
1109     insert_event_after(at_event, event);
1110     weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
1111   }
1112   return TRUE;
1113 }
1114 
1115 
insert_param_change_event_at(weed_plant_t * event_list,weed_plant_t * at_event,weed_plant_t * event)1116 void insert_param_change_event_at(weed_plant_t *event_list, weed_plant_t *at_event, weed_plant_t *event) {
1117   // insert event as last at same timecode as (FRAME_EVENT) at_event, before FRAME event
1118   weed_timecode_t tc = get_event_timecode(at_event);
1119   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
1120 
1121   //weed_add_plant_flags(event, WEED_LEAF_READONLY_PLUGIN); // protect it for interpolation
1122 
1123   while (at_event) {
1124     if (get_event_timecode(at_event) < tc) {
1125       if (!insert_event_after(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
1126       return;
1127     }
1128     if (WEED_EVENT_IS_FILTER_INIT(at_event)) {
1129       if (!insert_event_after(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
1130       return;
1131     }
1132     if (WEED_EVENT_IS_FRAME(at_event)) {
1133       if (!insert_event_before(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
1134       return;
1135     }
1136     at_event = get_prev_event(at_event);
1137   }
1138   at_event = get_first_event(event_list);
1139   insert_event_before(at_event, event);
1140   weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
1141 }
1142 
1143 
insert_frame_event_at(weed_plant_t * event_list,weed_timecode_t tc,int numframes,int * clips,int64_t * frames,weed_plant_t ** shortcut)1144 weed_plant_t *insert_frame_event_at(weed_plant_t *event_list, weed_timecode_t tc, int numframes, int *clips,
1145                                     int64_t *frames, weed_plant_t **shortcut) {
1146   // we will insert a FRAME event at timecode tc, after any other events (except for deinit events) at timecode tc
1147   // if there is an existing FRAME event at tc, we replace it with the new frame
1148 
1149   // shortcut can be a nearest guess of where the frame should be
1150 
1151   // returns NULL on memory error
1152 
1153   weed_plant_t *event = NULL, *new_event, *prev;
1154   weed_plant_t *new_event_list, *xevent_list;
1155   weed_timecode_t xtc;
1156   weed_error_t error;
1157   if (!event_list || !get_first_frame_event(event_list)) {
1158     // no existing event list, or no frames,  append
1159     event_list = append_frame_event(event_list, tc, numframes, clips, frames);
1160     if (!event_list) return NULL; // memory error
1161     if (shortcut) *shortcut = get_last_event(event_list);
1162     return event_list;
1163   }
1164 
1165   // skip the next part if we know we have to add at end
1166   if (tc <= get_event_timecode(get_last_event(event_list))) {
1167     if (shortcut && *shortcut) {
1168       event = *shortcut;
1169     } else event = get_first_event(event_list);
1170 
1171     if (get_event_timecode(event) > tc) {
1172       // step backwards until we get to a frame before where we want to add
1173       while (event && get_event_timecode(event) > tc) event = get_prev_frame_event(event);
1174       // event can come out NULL (add before first frame event), in which case we fall through
1175     } else {
1176       while (event && get_event_timecode(event) < tc) event = get_next_frame_event(event);
1177 
1178       // we reached the end, so we will add after last frame event
1179       if (!event) event = get_last_frame_event(event_list);
1180     }
1181 
1182     while (event && (((xtc = get_event_timecode(event)) < tc) || (xtc == tc && (!WEED_EVENT_IS_FILTER_DEINIT(event))))) {
1183       if (shortcut) *shortcut = event;
1184       if (xtc == tc && WEED_EVENT_IS_FRAME(event)) {
1185         error = weed_set_int_array(event, WEED_LEAF_CLIPS, numframes, clips);
1186         if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
1187         error = weed_set_int64_array(event, WEED_LEAF_FRAMES, numframes, frames);
1188         if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
1189         return event_list;
1190       }
1191       event = get_next_event(event);
1192     }
1193 
1194     // we passed all events in event_list; there was one or more at tc, but none were deinits or frames
1195     if (!event) {
1196       event = get_last_event(event_list);
1197       // event is after last event, append it
1198       if (!(xevent_list = append_frame_event(event_list, tc, numframes, clips, frames))) return NULL;
1199       event_list = xevent_list;
1200       if (shortcut) *shortcut = get_last_event(event_list);
1201       return event_list;
1202     }
1203   } else {
1204     // event is after last event, append it
1205     if (!(xevent_list = append_frame_event(event_list, tc, numframes, clips, frames))) return NULL;
1206     event_list = xevent_list;
1207     if (shortcut) *shortcut = get_last_event(event_list);
1208     return event_list;
1209   }
1210 
1211   // add frame before "event"
1212 
1213   if (!(new_event_list = append_frame_event(NULL, tc, numframes, clips, frames))) return NULL;
1214   // new_event_list is now an event_list with one frame event. We will steal its event and prepend it !
1215 
1216   new_event = get_first_event(new_event_list);
1217 
1218   prev = get_prev_event(event);
1219 
1220   if (prev) {
1221     error = weed_set_voidptr_value(prev, WEED_LEAF_NEXT, new_event);
1222     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
1223   }
1224   error = weed_set_voidptr_value(new_event, WEED_LEAF_PREVIOUS, prev);
1225   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
1226   error = weed_set_voidptr_value(new_event, WEED_LEAF_NEXT, event);
1227   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
1228   error = weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, new_event);
1229   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
1230 
1231   if (get_first_event(event_list) == event) {
1232     error = weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, new_event);
1233     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
1234   }
1235 
1236   weed_plant_free(new_event_list);
1237 
1238   if (shortcut) *shortcut = new_event;
1239   return event_list;
1240 }
1241 
1242 
insert_audio_event_at(weed_plant_t * event,int track,int clipnum,double seek,double vel)1243 void insert_audio_event_at(weed_plant_t *event, int track, int clipnum, double seek, double vel) {
1244   // insert/update audio event at (existing) frame event
1245   int *new_aclips;
1246   double *new_aseeks;
1247   double arv; // vel needs rounding to four dp (i don't know why, but otherwise we get some weird rounding errors)
1248 
1249   register int i;
1250 
1251   arv = (double)(myround(vel * 10000.)) / 10000.;
1252 
1253   if (WEED_EVENT_IS_AUDIO_FRAME(event)) {
1254     int *aclips = NULL;
1255     double *aseeks = NULL;
1256     int num_aclips = weed_frame_event_get_audio_tracks(event, &aclips, &aseeks);
1257 
1258     for (i = 0; i < num_aclips; i += 2) {
1259       if (aclips[i] == track) {
1260         if (clipnum <= 0) {
1261           if (num_aclips <= 2) {
1262             weed_leaf_delete(event, WEED_LEAF_AUDIO_CLIPS);
1263             weed_leaf_delete(event, WEED_LEAF_AUDIO_SEEKS);
1264             lives_freep((void **)&aseeks);
1265             lives_freep((void **)&aclips);
1266             return;
1267           } else {
1268             int *new_aclips = (int *)lives_malloc((num_aclips - 2) * sizint);
1269             double *new_aseeks = (double *)lives_malloc((num_aclips - 2) * sizdbl);
1270             int j, k = 0;
1271             for (j = 0; j < num_aclips; j += 2) {
1272               if (j != i) {
1273                 new_aclips[k] = aclips[j];
1274                 new_aclips[k + 1] = aclips[j + 1];
1275                 new_aseeks[k] = aseeks[j];
1276                 new_aseeks[k + 1] = aseeks[j + 1];
1277                 k += 2;
1278               }
1279             }
1280 
1281             weed_set_int_array(event, WEED_LEAF_AUDIO_CLIPS, num_aclips - 2, new_aclips);
1282             weed_set_double_array(event, WEED_LEAF_AUDIO_SEEKS, num_aclips - 2, new_aseeks);
1283             lives_free(new_aclips);
1284             lives_free(new_aseeks);
1285             lives_freep((void **)&aseeks);
1286             lives_freep((void **)&aclips);
1287             return;
1288           }
1289         }
1290 
1291         // update existing values
1292         aclips[i + 1] = clipnum;
1293         aseeks[i] = seek;
1294         aseeks[i + 1] = arv;
1295 
1296         weed_set_int_array(event, WEED_LEAF_AUDIO_CLIPS, num_aclips, aclips);
1297         weed_set_double_array(event, WEED_LEAF_AUDIO_SEEKS, num_aclips, aseeks);
1298         lives_freep((void **)&aseeks);
1299         lives_freep((void **)&aclips);
1300         return;
1301       }
1302     }
1303 
1304     if (clipnum <= 0) {
1305       lives_freep((void **)&aseeks);
1306       lives_freep((void **)&aclips);
1307       return;
1308     }
1309 
1310     // append
1311     new_aclips = (int *)lives_malloc((num_aclips + 2) * sizint);
1312     for (i = 0; i < num_aclips; i++) new_aclips[i] = aclips[i];
1313     new_aclips[i++] = track;
1314     new_aclips[i] = clipnum;
1315 
1316     new_aseeks = (double *)lives_malloc((num_aclips + 2) * sizdbl);
1317     for (i = 0; i < num_aclips; i++) new_aseeks[i] = aseeks[i];
1318     new_aseeks[i++] = seek;
1319     new_aseeks[i++] = arv;
1320 
1321     weed_set_int_array(event, WEED_LEAF_AUDIO_CLIPS, i, new_aclips);
1322     weed_set_double_array(event, WEED_LEAF_AUDIO_SEEKS, i, new_aseeks);
1323 
1324     lives_free(new_aclips);
1325     lives_free(new_aseeks);
1326 
1327     lives_freep((void **)&aseeks);
1328     lives_freep((void **)&aclips);
1329     return;
1330   }
1331   // create new values
1332 
1333   new_aclips = (int *)lives_malloc(2 * sizint);
1334   new_aclips[0] = track;
1335   new_aclips[1] = clipnum;
1336 
1337   new_aseeks = (double *)lives_malloc(2 * sizdbl);
1338   new_aseeks[0] = seek;
1339   new_aseeks[1] = arv;
1340 
1341   weed_set_int_array(event, WEED_LEAF_AUDIO_CLIPS, 2, new_aclips);
1342   weed_set_double_array(event, WEED_LEAF_AUDIO_SEEKS, 2, new_aseeks);
1343 
1344   lives_free(new_aclips);
1345   lives_free(new_aseeks);
1346 }
1347 
1348 
remove_audio_for_track(weed_plant_t * event,int track)1349 void remove_audio_for_track(weed_plant_t *event, int track) {
1350   // delete audio for a FRAME_EVENT with audio for specified track
1351   // if nothing left, delete the audio leaves
1352   int num_atracks;
1353   int *aclip_index = weed_get_int_array_counted(event, WEED_LEAF_AUDIO_CLIPS, &num_atracks);
1354   double *aseek_index = weed_get_double_array(event, WEED_LEAF_AUDIO_SEEKS, NULL);
1355   int *new_aclip_index = (int *)lives_malloc(num_atracks * sizint);
1356   double *new_aseek_index = (double *)lives_malloc(num_atracks * sizdbl);
1357 
1358   register int i, j = 0;
1359 
1360   for (i = 0; i < num_atracks; i += 2) {
1361     if (aclip_index[i] == track) continue;
1362     new_aclip_index[j] = aclip_index[i];
1363     new_aclip_index[j + 1] = aclip_index[i + 1];
1364     new_aseek_index[j] = aseek_index[i];
1365     new_aseek_index[j + 1] = aseek_index[i + 1];
1366     j += 2;
1367   }
1368   if (j == 0) {
1369     weed_leaf_delete(event, WEED_LEAF_AUDIO_CLIPS);
1370     weed_leaf_delete(event, WEED_LEAF_AUDIO_SEEKS);
1371   } else {
1372     weed_set_int_array(event, WEED_LEAF_AUDIO_CLIPS, j, new_aclip_index);
1373     weed_set_double_array(event, WEED_LEAF_AUDIO_SEEKS, j, new_aseek_index);
1374   }
1375   lives_free(aclip_index);
1376   lives_free(aseek_index);
1377   lives_free(new_aclip_index);
1378   lives_free(new_aseek_index);
1379 }
1380 
1381 
append_marker_event(weed_plant_t * event_list,weed_timecode_t tc,int marker_type)1382 weed_plant_t *append_marker_event(weed_plant_t *event_list, weed_timecode_t tc, int marker_type) {
1383   weed_plant_t *event, *prev;
1384 
1385   if (!event_list) {
1386     event_list = lives_event_list_new(NULL, NULL);
1387     if (!event_list) return NULL;
1388   }
1389 
1390   event = weed_plant_new(WEED_PLANT_EVENT);
1391   weed_set_voidptr_value(event, WEED_LEAF_NEXT, NULL);
1392 
1393   // TODO - error check
1394   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
1395   weed_set_int_value(event, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_MARKER);
1396 
1397   weed_set_int_value(event, WEED_LEAF_LIVES_TYPE, marker_type);
1398 
1399 #ifdef DEBUG_EVENTS
1400   g_print("adding marker event %p at tc %"PRId64"\n", init_events[0], tc);
1401 #endif
1402 
1403   if (!get_first_event(event_list)) {
1404     weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
1405     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, NULL);
1406   } else {
1407     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, get_last_event(event_list));
1408   }
1409   //weed_add_plant_flags(event, WEED_LEAF_READONLY_PLUGIN);
1410   prev = get_prev_event(event);
1411   if (prev) weed_set_voidptr_value(prev, WEED_LEAF_NEXT, event);
1412   weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
1413 
1414   return event_list;
1415 }
1416 
1417 
insert_marker_event_at(weed_plant_t * event_list,weed_plant_t * at_event,int marker_type,livespointer data)1418 weed_plant_t *insert_marker_event_at(weed_plant_t *event_list, weed_plant_t *at_event, int marker_type, livespointer data) {
1419   // insert marker event as first event at same timecode as (FRAME_EVENT) at_event
1420   weed_timecode_t tc = get_event_timecode(at_event);
1421   weed_plant_t *event = weed_plant_new(WEED_PLANT_EVENT);
1422   register int i;
1423 
1424   weed_set_int_value(event, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_MARKER);
1425   weed_set_int_value(event, WEED_LEAF_LIVES_TYPE, marker_type);
1426   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
1427 
1428   if (marker_type == EVENT_MARKER_BLOCK_START || marker_type == EVENT_MARKER_BLOCK_UNORDERED) {
1429     weed_set_int_value(event, WEED_LEAF_TRACKS, LIVES_POINTER_TO_INT(data));
1430   }
1431   //weed_add_plant_flags(event, WEED_LEAF_READONLY_PLUGIN);
1432 
1433   while (at_event) {
1434     at_event = get_prev_event(at_event);
1435     if (!at_event) break;
1436     switch (marker_type) {
1437     case EVENT_MARKER_BLOCK_START:
1438     case EVENT_MARKER_BLOCK_UNORDERED:
1439       if (WEED_EVENT_IS_MARKER(at_event) && (weed_get_int_value(at_event, WEED_LEAF_LIVES_TYPE, NULL) == marker_type)) {
1440         // add to existing event
1441         int num_tracks;
1442         int *tracks = weed_get_int_array_counted(at_event, WEED_LEAF_TRACKS, &num_tracks);
1443         int *new_tracks = (int *)lives_malloc((num_tracks + 1) * sizint);
1444         for (i = 0; i < num_tracks; i++) {
1445           new_tracks[i] = tracks[i];
1446         }
1447         new_tracks[i] = LIVES_POINTER_TO_INT(data); // add new track
1448         weed_set_int_array(at_event, WEED_LEAF_TRACKS, num_tracks + 1, new_tracks);
1449         lives_free(new_tracks);
1450         lives_free(tracks);
1451         weed_plant_free(event); // new event not used
1452         return event;
1453       }
1454       if (get_event_timecode(at_event) < tc) {
1455         // create new event
1456         if (!insert_event_after(at_event, event)) weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
1457         return event;
1458       }
1459       break;
1460     }
1461   }
1462 
1463   // event is first
1464   at_event = get_first_event(event_list);
1465   insert_event_before(at_event, event);
1466   weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
1467 
1468   return event;
1469 }
1470 
1471 
insert_blank_frame_event_at(weed_plant_t * event_list,weed_timecode_t tc,weed_plant_t ** shortcut)1472 LIVES_GLOBAL_INLINE weed_plant_t *insert_blank_frame_event_at(weed_plant_t *event_list, weed_timecode_t tc,
1473     weed_plant_t **shortcut) {
1474   int clip = -1;
1475   int64_t frame = 0;
1476   return insert_frame_event_at(event_list, tc, 1, &clip, &frame, shortcut);
1477 }
1478 
1479 
remove_filter_from_event_list(weed_plant_t * event_list,weed_plant_t * init_event)1480 void remove_filter_from_event_list(weed_plant_t *event_list, weed_plant_t *init_event) {
1481   int error;
1482   weed_plant_t *deinit_event = weed_get_plantptr_value(init_event, WEED_LEAF_DEINIT_EVENT, &error);
1483   weed_plant_t *event = init_event;
1484   weed_plant_t *filter_map = get_filter_map_before(init_event, LIVES_TRACK_ANY, NULL);
1485   void **new_init_events;
1486   weed_timecode_t deinit_tc = get_event_timecode(deinit_event);
1487   weed_plant_t *event_next;
1488 
1489   register int i;
1490 
1491   while (event && get_event_timecode(event) <= deinit_tc) {
1492     event_next = get_next_event(event);
1493     // update filter_maps
1494     if (WEED_EVENT_IS_FILTER_MAP(event)) {
1495       new_init_events = get_init_events_before(event, init_event, FALSE);
1496       for (i = 0; new_init_events[i]; i++);
1497       if (i == 0) weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENTS, NULL);
1498       else weed_set_voidptr_array(event, WEED_LEAF_INIT_EVENTS, i, new_init_events);
1499       lives_free(new_init_events);
1500 
1501       if ((!filter_map && i == 0) || (filter_map && compare_filter_maps(filter_map, event, LIVES_TRACK_ANY)))
1502         delete_event(event_list, event);
1503       else filter_map = event;
1504     }
1505     event = event_next;
1506   }
1507 
1508   // remove param_changes
1509   if (weed_plant_has_leaf(init_event, WEED_LEAF_IN_PARAMETERS)) {
1510     void *pchain_next;
1511     int num_params;
1512     void **pchain = weed_get_voidptr_array_counted(init_event, WEED_LEAF_IN_PARAMETERS, &num_params);
1513     for (i = 0; i < num_params; i++) {
1514       while (pchain[i]) {
1515         pchain_next = weed_get_voidptr_value((weed_plant_t *)pchain[i], WEED_LEAF_NEXT_CHANGE, NULL);
1516         delete_event(event_list, (weed_plant_t *)pchain[i]);
1517         pchain[i] = pchain_next;
1518       }
1519     }
1520     lives_free(pchain);
1521   }
1522 
1523   delete_event(event_list, init_event);
1524   delete_event(event_list, deinit_event);
1525 }
1526 
1527 
remove_event_from_filter_map(weed_plant_t * fmap,weed_plant_t * event)1528 static boolean remove_event_from_filter_map(weed_plant_t *fmap, weed_plant_t *event) {
1529   // return FALSE if result is NULL filter_map
1530   int num_inits;
1531   void **init_events = weed_get_voidptr_array_counted(fmap, WEED_LEAF_INIT_EVENTS, &num_inits);
1532   void **new_init_events;
1533 
1534   int i, j = 0;
1535 
1536   new_init_events = (void **)lives_malloc(num_inits * sizeof(void *));
1537   for (i = 0; i < num_inits; i++) {
1538     if (init_events[i] != event) new_init_events[j++] = init_events[i];
1539   }
1540 
1541   if (j == 0 || (j == 1 && (!event || !init_events[0]))) weed_set_voidptr_value(fmap, WEED_LEAF_INIT_EVENTS, NULL);
1542   else weed_set_voidptr_array(fmap, WEED_LEAF_INIT_EVENTS, j, new_init_events);
1543   lives_free(init_events);
1544   lives_free(new_init_events);
1545 
1546   return (!(j == 0 || (j == 1 && !event)));
1547 }
1548 
1549 
init_event_in_list(void ** init_events,int num_inits,weed_plant_t * event)1550 LIVES_GLOBAL_INLINE boolean init_event_in_list(void **init_events, int num_inits, weed_plant_t *event) {
1551   register int i;
1552   if (!init_events || !init_events[0]) return FALSE;
1553   for (i = 0; i < num_inits; i++) {
1554     if (init_events[i] == (void **)event) return TRUE;
1555   }
1556   return FALSE;
1557 }
1558 
1559 
filter_map_has_event(weed_plant_t * fmap,weed_plant_t * event)1560 boolean filter_map_has_event(weed_plant_t *fmap, weed_plant_t *event) {
1561   int num_inits;
1562   void **init_events = weed_get_voidptr_array_counted(fmap, WEED_LEAF_INIT_EVENTS, &num_inits);
1563   boolean ret = init_event_in_list(init_events, num_inits, event);
1564 
1565   lives_free(init_events);
1566   return ret;
1567 }
1568 
1569 
filter_init_has_owner(weed_plant_t * init_event,int track)1570 boolean filter_init_has_owner(weed_plant_t *init_event, int track) {
1571   int *owners;
1572   int num_owners;
1573   int i;
1574 
1575   if (!weed_plant_has_leaf(init_event, WEED_LEAF_IN_TRACKS)) return FALSE;
1576 
1577   owners = weed_get_int_array_counted(init_event, WEED_LEAF_IN_TRACKS, &num_owners);
1578 
1579   for (i = 0; i < num_owners; i++) {
1580     if (owners[i] == track) {
1581       lives_free(owners);
1582       return TRUE;
1583     }
1584   }
1585   lives_free(owners);
1586   return FALSE;
1587 }
1588 
1589 
backup_host_tags(weed_plant_t * event_list,weed_timecode_t curr_tc)1590 void backup_host_tags(weed_plant_t *event_list, weed_timecode_t curr_tc) {
1591   // when redrawing the current frame during rendering (in multitrack mode)
1592   // host keys will change (see backup_weed_instances() in effects-weed.c)
1593   // here we backup the host_tag (which maps a filter_init to a "key" and thus to an instance)
1594 
1595   weed_plant_t *event = get_first_event(event_list);
1596   weed_timecode_t tc;
1597 
1598   while (event && (tc = get_event_timecode(event)) <= curr_tc) {
1599     if (WEED_EVENT_IS_FILTER_INIT(event)) weed_leaf_copy(event, WEED_LEAF_HOST_TAG_COPY, event, WEED_LEAF_HOST_TAG);
1600     event = get_next_event(event);
1601   }
1602 }
1603 
1604 
restore_host_tags(weed_plant_t * event_list,weed_timecode_t curr_tc)1605 void restore_host_tags(weed_plant_t *event_list, weed_timecode_t curr_tc) {
1606   // when redrawing the current frame during rendering (in multitrack mode)
1607   // host keys will change (see backup_weed_instances() in effects-weed.c)
1608   // here we restore the host_tag (which maps a filter_init to a "key" and thus to an instance)
1609 
1610   weed_plant_t *event = get_first_event(event_list);
1611   weed_timecode_t tc;
1612 
1613   while (event && (tc = get_event_timecode(event)) <= curr_tc) {
1614     if (WEED_EVENT_IS_FILTER_INIT(event)) {
1615       weed_leaf_copy(event, WEED_LEAF_HOST_TAG, event, WEED_LEAF_HOST_TAG_COPY);
1616       weed_leaf_delete(event, WEED_LEAF_HOST_TAG_COPY);
1617     }
1618     event = get_next_event(event);
1619   }
1620 }
1621 
1622 
delete_param_changes_after_deinit(weed_plant_t * event_list,weed_plant_t * init_event)1623 void delete_param_changes_after_deinit(weed_plant_t *event_list, weed_plant_t *init_event) {
1624   // delete parameter value changes following the filter_deinit
1625   // this can be called when a FILTER_DEINIT is moved
1626   void **init_events;
1627   weed_plant_t *deinit_event = weed_get_plantptr_value(init_event, WEED_LEAF_DEINIT_EVENT, NULL);
1628   weed_timecode_t deinit_tc = get_event_timecode(deinit_event);
1629   weed_timecode_t pchain_tc;
1630 
1631   void *pchain, *pchain_next;
1632 
1633   int i, num_inits;
1634 
1635   if (!weed_plant_has_leaf(init_event, WEED_LEAF_IN_PARAMETERS)) return;
1636 
1637   init_events = weed_get_voidptr_array_counted(init_event, WEED_LEAF_IN_PARAMETERS, &num_inits);
1638 
1639   for (i = 0; i < num_inits; i++) {
1640     pchain = init_events[i];
1641     while (pchain) {
1642       pchain_tc = get_event_timecode((weed_plant_t *)pchain);
1643       if (!weed_plant_has_leaf((weed_plant_t *)pchain, WEED_LEAF_NEXT_CHANGE)) pchain_next = NULL;
1644       else pchain_next = weed_get_voidptr_value((weed_plant_t *)pchain, WEED_LEAF_NEXT_CHANGE, NULL);
1645       if (pchain_tc > deinit_tc) delete_event(event_list, (weed_plant_t *)pchain);
1646       pchain = pchain_next;
1647     }
1648   }
1649   lives_free(init_events);
1650 }
1651 
1652 
rescale_param_changes(weed_plant_t * event_list,weed_plant_t * init_event,weed_timecode_t new_init_tc,weed_plant_t * deinit_event,weed_timecode_t new_deinit_tc,double fps)1653 static void rescale_param_changes(weed_plant_t *event_list, weed_plant_t *init_event, weed_timecode_t new_init_tc,
1654                                   weed_plant_t *deinit_event, weed_timecode_t new_deinit_tc, double fps) {
1655   // rescale parameter value changes along the time axis
1656   // this can be called when a FILTER_INIT or FILTER_DEINIT is moved
1657 
1658   void **init_events;
1659 
1660   weed_timecode_t old_init_tc = get_event_timecode(init_event);
1661   weed_timecode_t old_deinit_tc = get_event_timecode(deinit_event);
1662   weed_timecode_t pchain_tc, new_tc;
1663 
1664   void *pchain;
1665   weed_plant_t *event;
1666 
1667   int num_inits, i;
1668 
1669   if (!weed_plant_has_leaf(init_event, WEED_LEAF_IN_PARAMETERS)) return;
1670 
1671   init_events = weed_get_voidptr_array_counted(init_event, WEED_LEAF_IN_PARAMETERS, &num_inits);
1672 
1673   if (!init_events) num_inits = 0;
1674 
1675   for (i = 0; i < num_inits; i++) {
1676     pchain = init_events[i];
1677     while (pchain) {
1678       pchain_tc = get_event_timecode((weed_plant_t *)pchain);
1679       new_tc = (weed_timecode_t)((double)(pchain_tc - old_init_tc) / (double)(old_deinit_tc - old_init_tc) *
1680                                  (double)(new_deinit_tc - new_init_tc)) + new_init_tc;
1681       new_tc = q_gint64(new_tc, fps);
1682       if (new_tc == pchain_tc) {
1683         if (!weed_plant_has_leaf((weed_plant_t *)pchain, WEED_LEAF_NEXT_CHANGE)) pchain = NULL;
1684         else pchain = weed_get_voidptr_value((weed_plant_t *)pchain, WEED_LEAF_NEXT_CHANGE, NULL);
1685         continue;
1686       }
1687       event = (weed_plant_t *)pchain;
1688       if (new_tc < pchain_tc) {
1689         while (event && get_event_timecode(event) > new_tc) event = get_prev_event(event);
1690       } else {
1691         while (event && get_event_timecode(event) < new_tc) event = get_next_event(event);
1692       }
1693 
1694       if (event) {
1695         unlink_event(event_list, (weed_plant_t *)pchain);
1696         insert_param_change_event_at(event_list, event, (weed_plant_t *)pchain);
1697       }
1698 
1699       if (!weed_plant_has_leaf((weed_plant_t *)pchain, WEED_LEAF_NEXT_CHANGE)) pchain = NULL;
1700       else pchain = weed_get_voidptr_value((weed_plant_t *)pchain, WEED_LEAF_NEXT_CHANGE, NULL);
1701     }
1702   }
1703 
1704   if (init_events) lives_free(init_events);
1705 }
1706 
1707 
is_in_hints(weed_plant_t * event,void ** hints)1708 static boolean is_in_hints(weed_plant_t *event, void **hints) {
1709   register int i;
1710   if (!hints) return FALSE;
1711   for (i = 0; hints[i]; i++) {
1712     if (hints[i] == event) return TRUE;
1713   }
1714   return FALSE;
1715 }
1716 
1717 
init_event_is_process_last(weed_plant_t * event)1718 boolean init_event_is_process_last(weed_plant_t *event) {
1719   weed_plant_t *filter;
1720   char *hashname;
1721 
1722   if (!event) return FALSE;
1723 
1724   hashname = weed_get_string_value(event, WEED_LEAF_FILTER, NULL);
1725   filter = get_weed_filter(weed_get_idx_for_hashname(hashname, TRUE));
1726   lives_free(hashname);
1727   return weed_filter_is_process_last(filter);
1728 }
1729 
1730 
add_init_event_to_filter_map(weed_plant_t * fmap,weed_plant_t * event,void ** hints)1731 void add_init_event_to_filter_map(weed_plant_t *fmap, weed_plant_t *event, void **hints) {
1732   // TODO - try to add at same position as in hints ***
1733 
1734   // init_events are the events we are adding to
1735   // event is what we are adding
1736 
1737   // hints is the init_events from the previous filter_map
1738 
1739   void **init_events, **new_init_events;
1740 
1741   boolean added = FALSE, plast = FALSE, mustadd = FALSE;
1742   int num_inits, i, j = 0;
1743 
1744   remove_event_from_filter_map(fmap, event);
1745 
1746   init_events = weed_get_voidptr_array_counted(fmap, WEED_LEAF_INIT_EVENTS, &num_inits);
1747 
1748   if (num_inits <= 1 && (!init_events || !init_events[0])) {
1749     weed_set_voidptr_value(fmap, WEED_LEAF_INIT_EVENTS, event);
1750     lives_free(init_events);
1751     return;
1752   }
1753 
1754   if (init_event_is_process_last(event)) plast = TRUE;
1755 
1756   new_init_events = (void **)lives_calloc((num_inits + 1), sizeof(void *));
1757 
1758   for (i = 0; i < num_inits; i++) {
1759     if (!added && init_event_is_process_last((weed_plant_t *)init_events[i])) mustadd = TRUE;
1760 
1761     if (mustadd || (!plast && !added && is_in_hints((weed_plant_t *)init_events[i], hints))) {
1762       new_init_events[j++] = event;
1763       added = TRUE;
1764     }
1765     if (init_events[i] == event) {
1766       if (!added) {
1767         added = TRUE;
1768         new_init_events[j++] = event;
1769       }
1770     } else {
1771       new_init_events[j++] = init_events[i];
1772     }
1773   }
1774   if (!added) {
1775     new_init_events[j++] = event;
1776   }
1777 
1778   weed_set_voidptr_array(fmap, WEED_LEAF_INIT_EVENTS, j, new_init_events);
1779   lives_free(init_events);
1780   lives_free(new_init_events);
1781 }
1782 
1783 
move_filter_init_event(weed_plant_t * event_list,weed_timecode_t new_tc,weed_plant_t * init_event,double fps)1784 void move_filter_init_event(weed_plant_t *event_list, weed_timecode_t new_tc, weed_plant_t *init_event, double fps) {
1785   int error, i, j = 0;
1786   weed_timecode_t tc = get_event_timecode(init_event);
1787   weed_plant_t *deinit_event = weed_get_plantptr_value(init_event, WEED_LEAF_DEINIT_EVENT, &error);
1788   weed_timecode_t deinit_tc = get_event_timecode(deinit_event);
1789   weed_plant_t *event = init_event, *event_next;
1790   weed_plant_t *filter_map, *copy_filter_map;
1791   void **init_events;
1792   int num_inits;
1793   void **event_types = NULL;
1794   boolean is_null_filter_map;
1795 
1796   rescale_param_changes(event_list, init_event, new_tc, deinit_event, deinit_tc, fps);
1797 
1798   if (new_tc > tc) {
1799     // moving right
1800     filter_map = get_filter_map_before(event, LIVES_TRACK_ANY, NULL);
1801     while (get_event_timecode(event) < new_tc) {
1802       event_next = get_next_event(event);
1803       if (WEED_EVENT_IS_FILTER_MAP(event)) {
1804         is_null_filter_map = !remove_event_from_filter_map(event, init_event);
1805         if ((!filter_map && is_null_filter_map) || (filter_map &&
1806             compare_filter_maps(filter_map, event, LIVES_TRACK_ANY)))
1807           delete_event(event_list, event);
1808         else filter_map = event;
1809       }
1810       event = event_next;
1811     }
1812     unlink_event(event_list, init_event);
1813     insert_filter_init_event_at(event_list, event, init_event);
1814 
1815     event = get_next_frame_event(init_event);
1816 
1817     init_events = get_init_events_before(event, init_event, TRUE);
1818     event_list = append_filter_map_event(event_list, new_tc, init_events);
1819     lives_free(init_events);
1820 
1821     filter_map = get_last_event(event_list);
1822     unlink_event(event_list, filter_map);
1823     insert_filter_map_event_at(event_list, event, filter_map, TRUE);
1824   } else {
1825     // moving left
1826     // see if event is switched on at start
1827     boolean is_on = FALSE;
1828     boolean adding = FALSE;
1829     while (event != deinit_event) {
1830       if (get_event_timecode(event) > tc) break;
1831       if (WEED_EVENT_IS_FILTER_MAP(event) && filter_map_has_event(event, init_event)) {
1832         if (weed_plant_has_leaf(event, WEED_LEAF_INIT_EVENTS)) {
1833           init_events = weed_get_voidptr_array_counted(event, WEED_LEAF_INIT_EVENTS, &num_inits);
1834           if (init_events[0]) {
1835             event_types = (void **)lives_malloc((num_inits + 1) * sizeof(void *));
1836             for (i = 0; i < num_inits; i++) {
1837               if (adding) event_types[j++] = init_events[i];
1838               if (init_events[i] == init_event) adding = TRUE;
1839             }
1840             event_types[j] = NULL;
1841             is_on = TRUE;
1842           }
1843           lives_free(init_events);
1844         }
1845         break;
1846       }
1847       event = get_next_event(event);
1848     }
1849     event = init_event;
1850     while (get_event_timecode(event) > new_tc) event = get_prev_event(event);
1851     unlink_event(event_list, init_event);
1852     insert_filter_init_event_at(event_list, event, init_event);
1853 
1854     if (is_on) {
1855       event = get_next_frame_event(init_event);
1856       filter_map = get_filter_map_before(event, LIVES_TRACK_ANY, NULL);
1857 
1858       // insert filter_map at new filter_init
1859       if (filter_map) {
1860         copy_filter_map = weed_plant_copy(filter_map);
1861         add_init_event_to_filter_map(copy_filter_map, init_event, event_types);
1862         filter_map = copy_filter_map;
1863       } else {
1864         init_events = (void **)lives_malloc(2 * sizeof(void *));
1865         init_events[0] = init_event;
1866         init_events[1] = NULL;
1867         event_list = append_filter_map_event(event_list, new_tc, init_events);
1868         lives_free(init_events);
1869         filter_map = get_last_event(event_list);
1870         unlink_event(event_list, filter_map);
1871       }
1872 
1873       insert_filter_map_event_at(event_list, event, filter_map, TRUE);
1874       event = get_next_event(filter_map);
1875 
1876       // ensure filter remains on until repositioned FILTER_INIT
1877       while (event && get_event_timecode(event) <= tc) {
1878         event_next = get_next_event(event);
1879         if (WEED_EVENT_IS_FILTER_MAP(event)) {
1880           add_init_event_to_filter_map(filter_map, init_event, event_types);
1881           if (compare_filter_maps(filter_map, event, LIVES_TRACK_ANY)) delete_event(event_list, event);
1882           else filter_map = event;
1883         }
1884         event = event_next;
1885       }
1886       if (event_types) lives_free(event_types);
1887     }
1888   }
1889 }
1890 
1891 
move_filter_deinit_event(weed_plant_t * event_list,weed_timecode_t new_tc,weed_plant_t * deinit_event,double fps,boolean rescale_pchanges)1892 void move_filter_deinit_event(weed_plant_t *event_list, weed_timecode_t new_tc, weed_plant_t *deinit_event,
1893                               double fps, boolean rescale_pchanges) {
1894   // move a filter_deinit from old pos to new pos, remove mention of it from filter maps,
1895   // possibly add/update filter map before frame at new_tc, remove duplicate filter_maps, update param_change events
1896   int error, i, j = 0;
1897   weed_timecode_t tc = get_event_timecode(deinit_event);
1898   weed_plant_t *init_event = (weed_plant_t *)weed_get_voidptr_value(deinit_event, WEED_LEAF_INIT_EVENT, &error);
1899   weed_timecode_t init_tc = get_event_timecode(init_event);
1900   weed_plant_t *event = deinit_event, *event_next;
1901   weed_plant_t *filter_map, *copy_filter_map;
1902   weed_plant_t *xevent;
1903   void **init_events;
1904   int num_inits;
1905   void **event_types = NULL;
1906   boolean is_null_filter_map;
1907 
1908   if (new_tc == tc) return;
1909 
1910   if (rescale_pchanges) rescale_param_changes(event_list, init_event, init_tc, deinit_event, new_tc, fps);
1911 
1912   if (new_tc < tc) {
1913     // moving left
1914     //find last event at new_tc, we are going to insert deinit_event after this
1915 
1916     // first find filter_map before new end position, copy it with filter removed
1917     while (get_event_timecode(event) > new_tc) event = get_prev_event(event);
1918     filter_map = get_filter_map_before(event, LIVES_TRACK_ANY, NULL);
1919     if (filter_map) {
1920       if (get_event_timecode(filter_map) != get_event_timecode(event)) {
1921         copy_filter_map = weed_plant_copy(filter_map);
1922         if (!WEED_EVENT_IS_FRAME(event)) event = get_prev_frame_event(event);
1923         if (!event) event = get_first_event(event_list);
1924         insert_filter_map_event_at(event_list, event, copy_filter_map, FALSE);
1925       } else copy_filter_map = filter_map;
1926       remove_event_from_filter_map(copy_filter_map, init_event);
1927       if (filter_map != copy_filter_map && compare_filter_maps(filter_map, copy_filter_map, LIVES_TRACK_ANY))
1928         delete_event(event_list, copy_filter_map);
1929       else filter_map = copy_filter_map;
1930     }
1931 
1932     while (!WEED_EVENT_IS_FRAME(event)) event = get_prev_event(event);
1933     xevent = event;
1934     filter_map = get_filter_map_before(event, LIVES_TRACK_ANY, NULL);
1935 
1936     // remove from following filter_maps
1937 
1938     while (event && get_event_timecode(event) <= tc) {
1939       event_next = get_next_event(event);
1940       if (WEED_EVENT_IS_FILTER_MAP(event)) {
1941         // found a filter map, so remove the event from it
1942         is_null_filter_map = !remove_event_from_filter_map(event, init_event);
1943         if ((!filter_map && is_null_filter_map) || (filter_map &&
1944             compare_filter_maps(filter_map, event, LIVES_TRACK_ANY)))
1945           delete_event(event_list, event);
1946         else filter_map = event;
1947       }
1948       event = event_next;
1949     }
1950     unlink_event(event_list, deinit_event);
1951     insert_filter_deinit_event_at(event_list, xevent, deinit_event);
1952     if (!rescale_pchanges) delete_param_changes_after_deinit(event_list, init_event);
1953   } else {
1954     // moving right
1955     // see if event is switched on at end
1956     boolean is_on = FALSE;
1957     boolean adding = FALSE;
1958 
1959     xevent = get_prev_event(deinit_event);
1960 
1961     // get event_types so we can add filter back at guess position
1962     filter_map = get_filter_map_before(deinit_event, LIVES_TRACK_ANY, NULL);
1963     if (filter_map && filter_map_has_event(filter_map, init_event)) {
1964       init_events = weed_get_voidptr_array_counted(filter_map, WEED_LEAF_INIT_EVENTS, &num_inits);
1965       event_types = (void **)lives_malloc((num_inits + 1) * sizeof(void *));
1966       for (i = 0; i < num_inits; i++) {
1967         if (adding) {
1968           event_types[j++] = init_events[i];
1969         }
1970         if (init_events[i] == init_event) adding = TRUE;
1971       }
1972       event_types[j] = NULL;
1973       is_on = TRUE;
1974       lives_free(init_events);
1975     }
1976 
1977     // move deinit event
1978     event = deinit_event;
1979     while (event && get_event_timecode(event) < new_tc) event = get_next_event(event);
1980 
1981     unlink_event(event_list, deinit_event);
1982 
1983     if (!event) return;
1984 
1985     insert_filter_deinit_event_at(event_list, event, deinit_event);
1986 
1987     if (is_on) {
1988       // ensure filter remains on until new position
1989       event = xevent;
1990       while (event != deinit_event) {
1991         if (get_event_timecode(event) == new_tc && WEED_EVENT_IS_FRAME(event)) break;
1992         event_next = get_next_event(event);
1993         if (WEED_EVENT_IS_FILTER_MAP(event)) {
1994           add_init_event_to_filter_map(event, init_event, event_types);
1995           if (compare_filter_maps(filter_map, event, LIVES_TRACK_ANY)) delete_event(event_list, event);
1996           else filter_map = event;
1997         }
1998         event = event_next;
1999       }
2000       if (event_types) lives_free(event_types);
2001 
2002       // find last FILTER_MAP before deinit_event
2003       event = deinit_event;
2004       while (event && get_event_timecode(event) == new_tc) event = get_next_event(event);
2005       if (!event) event = get_last_event(event_list);
2006       filter_map = get_filter_map_before(event, LIVES_TRACK_ANY, NULL);
2007 
2008       if (filter_map && filter_map_has_event(filter_map, init_event)) {
2009         // if last FILTER_MAP before deinit_event mentions init_event, remove init_event,
2010         // insert FILTER_MAP after deinit_event
2011         copy_filter_map = weed_plant_copy(filter_map);
2012 
2013         remove_event_from_filter_map(copy_filter_map, init_event);
2014         insert_filter_map_event_at(event_list, deinit_event, copy_filter_map, FALSE);
2015         event = get_next_event(copy_filter_map);
2016         while (event) {
2017           // remove next FILTER_MAP if it is a duplicate
2018           if (WEED_EVENT_IS_FILTER_MAP(event)) {
2019             if (compare_filter_maps(copy_filter_map, event, LIVES_TRACK_ANY)) delete_event(event_list, event);
2020             break;
2021           }
2022           event = get_next_event(event);
2023 	  // *INDENT-OFF*
2024         }}}}
2025   // *INDENT-ON*
2026 
2027 }
2028 
2029 
move_event_right(weed_plant_t * event_list,weed_plant_t * event,boolean can_stay,double fps)2030 boolean move_event_right(weed_plant_t *event_list, weed_plant_t *event, boolean can_stay, double fps) {
2031   // move a filter_init or param_change to the right
2032   // this can happen for two reasons: - we are rectifying an event_list, or a block was deleted or moved
2033 
2034   // if can_stay is FALSE, event is forced to move. This is used only during event list rectification.
2035 
2036   weed_timecode_t tc = get_event_timecode(event), new_tc = tc;
2037   weed_plant_t *xevent = event;
2038 
2039   int *owners = NULL;
2040 
2041   boolean all_ok = FALSE;
2042 
2043   int num_owners = 0, num_clips, i;
2044 
2045   if (WEED_EVENT_IS_FILTER_INIT(event)) {
2046     owners = weed_get_int_array_counted(event, WEED_LEAF_IN_TRACKS, &num_owners);
2047   } else if (!WEED_EVENT_IS_PARAM_CHANGE(event)) return TRUE;
2048 
2049   if (num_owners > 0) {
2050     while (xevent) {
2051       if (WEED_EVENT_IS_FRAME(xevent)) {
2052         if ((new_tc = get_event_timecode(xevent)) > tc || (can_stay && new_tc == tc)) {
2053           all_ok = TRUE;
2054           num_clips = weed_leaf_num_elements(xevent, WEED_LEAF_CLIPS);
2055           // find timecode of next event which has valid frames at all owner tracks
2056           for (i = 0; i < num_owners; i++) {
2057             if (owners[i] < 0) continue; // ignore audio owners
2058             if (num_clips <= owners[i] || get_frame_event_clip(xevent, owners[i]) < 0 || get_frame_event_frame(xevent, owners[i]) < 1) {
2059               all_ok = FALSE;
2060               break; // blank frame, or not enough frames
2061             }
2062           }
2063           if (all_ok) break;
2064         }
2065       }
2066       xevent = get_next_event(xevent);
2067     }
2068     lives_free(owners);
2069   } else {
2070     if (can_stay) return TRUE; // bound to timeline, and allowed to stay
2071     xevent = get_next_frame_event(event); // bound to timeline, move to next frame event
2072     new_tc = get_event_timecode(xevent);
2073   }
2074 
2075   if (can_stay && (new_tc == tc) && all_ok) return TRUE;
2076 
2077   // now we have xevent, new_tc
2078 
2079   if (WEED_EVENT_IS_FILTER_INIT(event)) {
2080     weed_plant_t *deinit_event = weed_get_plantptr_value(event, WEED_LEAF_DEINIT_EVENT, NULL);
2081     if (!xevent || get_event_timecode(deinit_event) < new_tc) {
2082       // if we are moving a filter_init past its deinit event, remove it, remove deinit, remove param_change events,
2083       // remove from all filter_maps, and check for duplicate filter maps
2084       remove_filter_from_event_list(event_list, event);
2085       return FALSE;
2086     }
2087     move_filter_init_event(event_list, new_tc, event, fps);
2088   } else {
2089     // otherwise, for a param_change, just insert it at new_tc
2090     weed_plant_t *init_event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
2091     weed_plant_t *deinit_event = weed_get_plantptr_value(init_event, WEED_LEAF_DEINIT_EVENT, NULL);
2092     if (!xevent || get_event_timecode(deinit_event) < new_tc) {
2093       delete_event(event_list, event);
2094       return FALSE;
2095     }
2096     unlink_event(event_list, event);
2097     insert_param_change_event_at(event_list, xevent, event);
2098   }
2099   return FALSE;
2100 }
2101 
2102 
move_event_left(weed_plant_t * event_list,weed_plant_t * event,boolean can_stay,double fps)2103 boolean move_event_left(weed_plant_t *event_list, weed_plant_t *event, boolean can_stay, double fps) {
2104   // move a filter_deinit to the left
2105   // this can happen for two reasons: - we are rectifying an event_list, or a block was deleted or moved
2106 
2107   // if can_stay is FALSE, event is forced to move. This is used only during event list rectification.
2108 
2109   weed_timecode_t tc = get_event_timecode(event), new_tc = tc;
2110   weed_plant_t *xevent = event;
2111   weed_plant_t *init_event;
2112 
2113   int *owners;
2114 
2115   boolean all_ok = FALSE;
2116 
2117   int num_owners = 0, num_clips, i;
2118 
2119   if (WEED_EVENT_IS_FILTER_DEINIT(event))
2120     init_event = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
2121   else return TRUE;
2122 
2123   owners = weed_get_int_array_counted(init_event, WEED_LEAF_IN_TRACKS, &num_owners);
2124 
2125   if (num_owners > 0) {
2126     while (xevent) {
2127       if (WEED_EVENT_IS_FRAME(xevent)) {
2128         if ((new_tc = get_event_timecode(xevent)) < tc || (can_stay && new_tc == tc)) {
2129           all_ok = TRUE;
2130           // find timecode of previous event which has valid frames at all owner tracks
2131           for (i = 0; i < num_owners; i++) {
2132             if (owners[i] < 0) continue; // ignore audio owners
2133             num_clips = weed_leaf_num_elements(xevent, WEED_LEAF_CLIPS);
2134             if (num_clips <= owners[i] || get_frame_event_clip(xevent, owners[i]) < 0 || get_frame_event_frame(xevent, owners[i]) < 1) {
2135               all_ok = FALSE;
2136               break; // blank frame
2137             }
2138           }
2139           if (all_ok) break;
2140         }
2141       }
2142       xevent = get_prev_event(xevent);
2143     }
2144     lives_freep((void **)&owners);
2145   } else {
2146     if (can_stay) return TRUE; // bound to timeline, and allowed to stay
2147     while (xevent) {
2148       // bound to timeline, just move to previous tc
2149       if ((new_tc = get_event_timecode(xevent)) < tc) break;
2150       xevent = get_prev_event(xevent);
2151     }
2152   }
2153   // now we have new_tc
2154 
2155   if (can_stay && (new_tc == tc) && all_ok) return TRUE;
2156 
2157   if (get_event_timecode(init_event) > new_tc) {
2158     // if we are moving a filter_deinit past its init event, remove it, remove init, remove param_change events,
2159     // remove from all filter_maps, and check for duplicate filter maps
2160     remove_filter_from_event_list(event_list, init_event);
2161     return FALSE;
2162   }
2163 
2164   // otherwise, go from old pos to new pos, remove mention of it from filter maps, possibly add/update filter map as last event at new_tc,
2165   // remove duplicate filter_maps, update param_change events
2166 
2167   move_filter_deinit_event(event_list, new_tc, event, fps, TRUE);
2168 
2169   return FALSE;
2170 }
2171 
2172 
2173 //////////////////////////////////////////////////////
2174 // rendering
2175 
set_render_choice(LiVESToggleButton * togglebutton,livespointer choice)2176 void set_render_choice(LiVESToggleButton * togglebutton, livespointer choice) {
2177   if (lives_toggle_button_get_active(togglebutton)) render_choice = LIVES_POINTER_TO_INT(choice);
2178 }
2179 
2180 
set_render_choice_button(LiVESButton * button,livespointer choice)2181 void set_render_choice_button(LiVESButton * button, livespointer choice) {
2182   render_choice = LIVES_POINTER_TO_INT(choice);
2183 }
2184 
2185 
events_rec_dialog(void)2186 LiVESWidget *events_rec_dialog(void) {
2187   LiVESWidget *e_rec_dialog;
2188   LiVESWidget *dialog_vbox;
2189   LiVESWidget *vbox;
2190   LiVESWidget *hbox;
2191   LiVESWidget *label;
2192   LiVESWidget *radiobutton;
2193   LiVESWidget *okbutton;
2194   LiVESWidget *cancelbutton;
2195   LiVESSList *radiobutton_group = NULL;
2196   LiVESAccelGroup *accel_group;
2197 
2198   render_choice = RENDER_CHOICE_PREVIEW;
2199 
2200   e_rec_dialog = lives_standard_dialog_new(_("Events Recorded"), FALSE, -1, -1);
2201 
2202   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(e_rec_dialog));
2203 
2204   vbox = lives_vbox_new(FALSE, widget_opts.packing_height * 4);
2205   lives_box_pack_start(LIVES_BOX(dialog_vbox), vbox, TRUE, TRUE, 0);
2206 
2207   label = lives_standard_label_new(_("Events were recorded. What would you like to do with them ?"));
2208 
2209   lives_box_pack_start(LIVES_BOX(vbox), label, TRUE, TRUE, 0);
2210 
2211   hbox = lives_hbox_new(FALSE, 0);
2212   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
2213 
2214   radiobutton = lives_standard_radio_button_new(_("_Preview events"), &radiobutton_group, LIVES_BOX(hbox), NULL);
2215 
2216   lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(radiobutton), TRUE);
2217 
2218   lives_signal_sync_connect(LIVES_GUI_OBJECT(radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2219                             LIVES_GUI_CALLBACK(set_render_choice),
2220                             LIVES_INT_TO_POINTER(RENDER_CHOICE_PREVIEW));
2221 
2222   if (!mainw->clip_switched && CURRENT_CLIP_IS_NORMAL && !mainw->recording_recovered
2223       && (last_rec_start_tc == -1 || ((double)last_rec_start_tc / TICKS_PER_SECOND_DBL) < (cfile->frames - 1.) / cfile->fps)) {
2224     hbox = lives_hbox_new(FALSE, 0);
2225     lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
2226 
2227     radiobutton = lives_standard_radio_button_new(_("Render events to _same clip"), &radiobutton_group, LIVES_BOX(hbox), NULL);
2228 
2229     lives_signal_sync_connect(LIVES_GUI_OBJECT(radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2230                               LIVES_GUI_CALLBACK(set_render_choice),
2231                               LIVES_INT_TO_POINTER(RENDER_CHOICE_SAME_CLIP));
2232   }
2233 
2234   hbox = lives_hbox_new(FALSE, 0);
2235   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
2236 
2237   radiobutton = lives_standard_radio_button_new(_("Render events to _new clip"), &radiobutton_group, LIVES_BOX(hbox), NULL);
2238 
2239   lives_signal_sync_connect(LIVES_GUI_OBJECT(radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2240                             LIVES_GUI_CALLBACK(set_render_choice),
2241                             LIVES_INT_TO_POINTER(RENDER_CHOICE_NEW_CLIP));
2242 
2243 #ifdef LIBAV_TRANSCODE
2244   hbox = lives_hbox_new(FALSE, 0);
2245   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
2246 
2247   radiobutton = lives_standard_radio_button_new(_("Quick transcode to video clip"), &radiobutton_group, LIVES_BOX(hbox), NULL);
2248 
2249   lives_signal_sync_connect(LIVES_GUI_OBJECT(radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2250                             LIVES_GUI_CALLBACK(set_render_choice),
2251                             LIVES_INT_TO_POINTER(RENDER_CHOICE_TRANSCODE));
2252 #endif
2253 
2254   hbox = lives_hbox_new(FALSE, 0);
2255   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
2256 
2257   radiobutton = lives_standard_radio_button_new(_("View/edit events in _multitrack window (test)"), &radiobutton_group,
2258                 LIVES_BOX(hbox), NULL);
2259 
2260   lives_signal_sync_connect(LIVES_GUI_OBJECT(radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2261                             LIVES_GUI_CALLBACK(set_render_choice),
2262                             LIVES_INT_TO_POINTER(RENDER_CHOICE_MULTITRACK));
2263 
2264   if (mainw->stored_event_list) lives_widget_set_no_show_all(hbox, TRUE);
2265 
2266   hbox = lives_hbox_new(FALSE, 0);
2267   lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
2268 
2269   radiobutton = lives_standard_radio_button_new(_("View/edit events in _event window"), &radiobutton_group, LIVES_BOX(hbox),
2270                 NULL);
2271 
2272   add_fill_to_box(LIVES_BOX(vbox));
2273 
2274   lives_signal_sync_connect(LIVES_GUI_OBJECT(radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2275                             LIVES_GUI_CALLBACK(set_render_choice),
2276                             LIVES_INT_TO_POINTER(RENDER_CHOICE_EVENT_LIST));
2277 
2278   cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(e_rec_dialog), LIVES_STOCK_CANCEL, NULL,
2279                  LIVES_RESPONSE_CANCEL);
2280 
2281   lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
2282                             LIVES_GUI_CALLBACK(set_render_choice_button),
2283                             LIVES_INT_TO_POINTER(RENDER_CHOICE_DISCARD));
2284 
2285   accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
2286   lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
2287                                LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
2288 
2289   lives_window_add_accel_group(LIVES_WINDOW(e_rec_dialog), accel_group);
2290 
2291   okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(e_rec_dialog), LIVES_STOCK_OK, NULL,
2292              LIVES_RESPONSE_OK);
2293 
2294   lives_button_grab_default_special(okbutton);
2295 
2296   return e_rec_dialog;
2297 }
2298 
2299 
event_list_free_events(weed_plant_t * event_list)2300 static void event_list_free_events(weed_plant_t *event_list) {
2301   weed_plant_t *event, *next_event;
2302   event = get_first_event(event_list);
2303 
2304   while (event) {
2305     next_event = get_next_event(event);
2306     if (mainw->multitrack && event_list == mainw->multitrack->event_list) mt_fixup_events(mainw->multitrack, event, NULL);
2307     weed_plant_free(event);
2308     event = next_event;
2309   }
2310 }
2311 
2312 
event_list_free(weed_plant_t * event_list)2313 void event_list_free(weed_plant_t *event_list) {
2314   if (!event_list) return;
2315   event_list_free_events(event_list);
2316   weed_plant_free(event_list);
2317 }
2318 
2319 
event_list_replace_events(weed_plant_t * event_list,weed_plant_t * new_event_list)2320 void event_list_replace_events(weed_plant_t *event_list, weed_plant_t *new_event_list) {
2321   if (!event_list || !new_event_list) return;
2322   if (event_list == new_event_list) return;
2323   event_list_free_events(event_list);
2324   weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, get_first_event(new_event_list));
2325   weed_set_voidptr_value(event_list, WEED_LEAF_LAST, get_last_event(new_event_list));
2326 }
2327 
2328 
event_list_to_block(weed_plant_t * event_list,int num_events)2329 boolean event_list_to_block(weed_plant_t *event_list, int num_events) {
2330   // translate our new event_list to older event blocks
2331   // by now we should have eliminated clip switches and param settings
2332 
2333   // first we count the frame events
2334   weed_plant_t *event;
2335   char *what;
2336   LiVESResponseType response;
2337   int i = 0;
2338 
2339   if (!event_list) return TRUE;
2340   what = (_("memory for the reordering operation"));
2341   // then we create event_frames
2342   do {
2343     response = LIVES_RESPONSE_OK;
2344     if (!create_event_space(num_events)) {
2345       response = do_memory_error_dialog(what, num_events * sizeof(resample_event));
2346     }
2347   } while (response == LIVES_RESPONSE_RETRY);
2348   lives_free(what);
2349   if (response == LIVES_RESPONSE_CANCEL) {
2350     return FALSE;
2351   }
2352 
2353   event = get_first_event(event_list);
2354 
2355   while (event) {
2356     if (WEED_EVENT_IS_FRAME(event)) {
2357       (cfile->resample_events + i++)->value = (int)weed_get_int64_value(event, WEED_LEAF_FRAMES, NULL);
2358     }
2359     event = get_next_event(event);
2360   }
2361   return TRUE;
2362 }
2363 
2364 
event_list_close_gaps(weed_event_t * event_list)2365 void event_list_close_gaps(weed_event_t *event_list) {
2366   // close gap at start of event list, and between record_end and record_start markers
2367   weed_event_t *event, *next_event, *first_event;
2368   weed_timecode_t tc = 0, tc_delta = 0, rec_end_tc = 0, tc_start = 0, tc_offs = 0, last_tc = 0;
2369   int marker_type;
2370 
2371   if (!mainw->clip_switched) {
2372     /// offset of recording in clip
2373     tc_start = calc_time_from_frame(mainw->current_file, mainw->play_start) * TICKS_PER_SECOND_DBL;
2374   }
2375 
2376   if (!event_list) return;
2377 
2378   event = get_first_event(event_list);
2379   if (WEED_PLANT_IS_EVENT_LIST(event)) event = get_next_event(event);
2380 
2381   first_event = event;
2382 
2383   if (event) tc_offs = get_event_timecode(event);
2384 
2385   while (event) {
2386     next_event = get_next_event(event);
2387     last_tc = tc;
2388     tc = get_event_timecode(event) - tc_offs;
2389     if (tc < 0) tc = 0;
2390 
2391     if (weed_plant_has_leaf(event, WEED_LEAF_TC_ADJUSTMENT)) {
2392       if (next_event) {
2393         if (!weed_plant_has_leaf(next_event, WEED_LEAF_TC_ADJUSTMENT)) {
2394           weed_timecode_t ntc = get_event_timecode(next_event);
2395           tc_offs = weed_get_int64_value(event, WEED_LEAF_TC_ADJUSTMENT, NULL);
2396           if (tc + tc_offs > ntc) {
2397             tc -= tc_offs;
2398           }
2399         }
2400       } else if (last_tc + tc_offs <= tc) tc -= tc_offs;
2401     }
2402 
2403     if (WEED_EVENT_IS_MARKER(event)) {
2404       marker_type = weed_get_int_value(event, WEED_LEAF_LIVES_TYPE, NULL);
2405       if (marker_type == EVENT_MARKER_RECORD_END) {
2406         rec_end_tc = tc;
2407         delete_event(event_list, event);
2408         event = next_event;
2409         continue;
2410       } else if (marker_type == EVENT_MARKER_RECORD_START) {
2411         // squash gaps in recording, bu we note the gap for output to same clip
2412         tc_delta += tc - rec_end_tc;
2413 
2414         if (!mainw->clip_switched) {
2415           /// if this is > clip length we cannnot render to same clip
2416           last_rec_start_tc = tc + tc_start;
2417           // if rendering to same clip, we will pick up this value and add it to the out frame tc
2418           weed_set_int64_value(event, WEED_LEAF_TCDELTA, tc_delta + tc_start);
2419         }
2420       }
2421     }
2422 
2423     /// subtract delta to close gaps, we already subtracted tc_offs
2424     tc -= tc_delta;
2425     weed_set_int64_value(event, WEED_LEAF_TC_ADJUSTMENT, tc_delta + tc_offs);
2426     weed_event_set_timecode(event, tc);
2427     event = next_event;
2428   }
2429   for (event = first_event; event; event = get_next_event(event)) {
2430     weed_leaf_delete(event, WEED_LEAF_TC_ADJUSTMENT);
2431   }
2432 }
2433 
2434 
add_track_to_avol_init(weed_plant_t * filter,weed_plant_t * event,int nbtracks,boolean behind)2435 void add_track_to_avol_init(weed_plant_t *filter, weed_plant_t *event, int nbtracks, boolean behind) {
2436   // added a new video track - now we need to update our audio volume and pan effect
2437   weed_plant_t **in_ptmpls;
2438   void **pchainx, *pchange;
2439 
2440   int *new_in_tracks;
2441   int *igns, *nigns;
2442 
2443   int num_in_tracks, x = -nbtracks;
2444   int nparams, numigns;
2445 
2446   int bval, i, j;
2447 
2448   // add a new value to in_tracks
2449   num_in_tracks = weed_leaf_num_elements(event, WEED_LEAF_IN_TRACKS) + 1;
2450   new_in_tracks = (int *)lives_malloc(num_in_tracks * sizint);
2451   for (i = 0; i < num_in_tracks; i++) {
2452     new_in_tracks[i] = x++;
2453   }
2454   weed_set_int_array(event, WEED_LEAF_IN_TRACKS, num_in_tracks, new_in_tracks);
2455   lives_free(new_in_tracks);
2456 
2457   weed_set_int_value(event, WEED_LEAF_IN_COUNT, weed_get_int_value(event, WEED_LEAF_IN_COUNT, NULL) + 1);
2458 
2459   // update all param_changes
2460 
2461   pchainx = weed_get_voidptr_array_counted(event, WEED_LEAF_IN_PARAMETERS, &nparams);
2462 
2463   in_ptmpls = weed_get_plantptr_array(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, NULL);
2464 
2465   for (i = 0; i < nparams; i++) {
2466     pchange = (weed_plant_t *)pchainx[i];
2467     bval = WEED_FALSE;
2468     while (pchange) {
2469       fill_param_vals_to((weed_plant_t *)pchange, in_ptmpls[i], behind ? num_in_tracks - 1 : 1);
2470       if (weed_plant_has_leaf((weed_plant_t *)pchange, WEED_LEAF_IGNORE)) {
2471         igns = weed_get_boolean_array_counted((weed_plant_t *)pchange, WEED_LEAF_IGNORE, &numigns);
2472         nigns = (int *)lives_malloc(++numigns * sizint);
2473 
2474         for (j = 0; j < numigns; j++) {
2475           if (behind) {
2476             if (j < numigns - 1) nigns[j] = igns[j];
2477             else nigns[j] = bval;
2478           } else {
2479             if (j == 0) nigns[j] = igns[j];
2480             else if (j == 1) nigns[j] = bval;
2481             else nigns[j] = igns[j - 1];
2482           }
2483         }
2484         weed_set_boolean_array((weed_plant_t *)pchange, WEED_LEAF_IGNORE, numigns, nigns);
2485         lives_free(igns);
2486         lives_free(nigns);
2487       }
2488       pchange = weed_get_voidptr_value((weed_plant_t *)pchange, WEED_LEAF_NEXT_CHANGE, NULL);
2489       bval = WEED_TRUE;
2490     }
2491   }
2492 
2493   lives_free(in_ptmpls);
2494 }
2495 
2496 
event_list_add_track(weed_plant_t * event_list,int layer)2497 void event_list_add_track(weed_plant_t *event_list, int layer) {
2498   // in this function we insert a new track before existing tracks
2499   // TODO - memcheck
2500   weed_plant_t *event;
2501 
2502   int *clips, *newclips, i;
2503   int64_t *frames, *newframes;
2504   int *in_tracks, *out_tracks;
2505 
2506   int num_in_tracks, num_out_tracks;
2507   int numframes;
2508 
2509   if (!event_list) return;
2510 
2511   event = get_first_event(event_list);
2512   while (event) {
2513     switch (get_event_type(event)) {
2514     case WEED_EVENT_TYPE_FRAME:
2515       clips = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &numframes);
2516       frames = weed_get_int64_array(event, WEED_LEAF_FRAMES, NULL);
2517       if (numframes == 1 && clips[0] == -1 && frames[0] == 0) {
2518         // for blank frames, we don't do anything
2519         lives_free(clips);
2520         lives_free(frames);
2521         break;
2522       }
2523 
2524       newclips = (int *)lives_malloc((numframes + 1) * sizint);
2525       newframes = (int64_t *)lives_malloc((numframes + 1) * 8);
2526 
2527       newclips[layer] = -1;
2528       newframes[layer] = 0;
2529       for (i = 0; i < numframes; i++) {
2530         if (i < layer) {
2531           newclips[i] = clips[i];
2532           newframes[i] = frames[i];
2533         } else {
2534           newclips[i + 1] = clips[i];
2535           newframes[i + 1] = frames[i];
2536         }
2537       }
2538       numframes++;
2539 
2540       weed_set_int_array(event, WEED_LEAF_CLIPS, numframes, newclips);
2541       weed_set_int64_array(event, WEED_LEAF_FRAMES, numframes, newframes);
2542 
2543       lives_free(newclips);
2544       lives_free(newframes);
2545       lives_free(clips);
2546       lives_free(frames);
2547 
2548       if (WEED_EVENT_IS_AUDIO_FRAME(event)) {
2549         int *aclips = NULL;
2550         int atracks = weed_frame_event_get_audio_tracks(event, &aclips, NULL);
2551         for (i = 0; i < atracks; i += 2) {
2552           if (aclips[i] >= 0) aclips[i]++;
2553         }
2554         weed_set_int_array(event, WEED_LEAF_AUDIO_CLIPS, atracks, aclips);
2555         lives_free(aclips);
2556       }
2557       break;
2558 
2559     case WEED_EVENT_TYPE_FILTER_INIT:
2560       in_tracks = weed_get_int_array_counted(event, WEED_LEAF_IN_TRACKS, &num_in_tracks);
2561       if (num_in_tracks) {
2562         for (i = 0; i < num_in_tracks; i++) {
2563           if (in_tracks[i] >= layer) in_tracks[i]++;
2564         }
2565         weed_set_int_array(event, WEED_LEAF_IN_TRACKS, num_in_tracks, in_tracks);
2566         lives_free(in_tracks);
2567       }
2568       out_tracks = weed_get_int_array_counted(event, WEED_LEAF_OUT_TRACKS, &num_out_tracks);
2569       if (num_out_tracks) {
2570         for (i = 0; i < num_out_tracks; i++) {
2571           if (out_tracks[i] >= layer) out_tracks[i]++;
2572         }
2573         weed_set_int_array(event, WEED_LEAF_OUT_TRACKS, num_out_tracks, out_tracks);
2574         lives_free(out_tracks);
2575       }
2576       break;
2577     }
2578     event = get_next_event(event);
2579   }
2580 }
2581 
2582 
create_frame_event(weed_timecode_t tc,int numframes,int * clips,int64_t * frames)2583 static weed_plant_t *create_frame_event(weed_timecode_t tc, int numframes, int *clips, int64_t *frames) {
2584   weed_error_t error;
2585   weed_plant_t *event;
2586 
2587   event = weed_plant_new(WEED_PLANT_EVENT);
2588   if (!event) return NULL;
2589   error = weed_set_voidptr_value(event, WEED_LEAF_NEXT, NULL);
2590   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2591   error = weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, NULL);
2592   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2593 
2594   ////////////////////////////////////////
2595 
2596   error = weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
2597   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2598   error = weed_set_int_value(event, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_FRAME);
2599   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2600 
2601   error = weed_set_int_array(event, WEED_LEAF_CLIPS, numframes, clips);
2602   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2603   error = weed_set_int64_array(event, WEED_LEAF_FRAMES, numframes, frames);
2604   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2605 
2606   return event;
2607 }
2608 
2609 
append_frame_event(weed_plant_t * event_list,weed_timecode_t tc,int numframes,int * clips,int64_t * frames)2610 weed_plant_t *append_frame_event(weed_plant_t *event_list, weed_timecode_t tc, int numframes, int *clips, int64_t *frames) {
2611   // append a frame event to an event_list
2612   weed_plant_t *event, *prev;
2613   weed_error_t error;
2614   // returns NULL on memory error
2615 
2616   ///////////// TODO - to func //////////////
2617   if (!event_list) {
2618     event_list = lives_event_list_new(NULL, NULL);
2619     //weed_add_plant_flags(event_list, WEED_LEAF_READONLY_PLUGIN);
2620   }
2621 
2622   event = create_frame_event(tc, numframes, clips, frames);
2623   if (!event) return NULL;
2624 
2625   if (!get_first_event(event_list)) {
2626     error = weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
2627     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2628     error = weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, NULL);
2629     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2630   } else {
2631     error = weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, get_last_event(event_list));
2632     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2633   }
2634   //weed_add_plant_flags(event, WEED_LEAF_READONLY_PLUGIN);
2635   prev = get_prev_event(event);
2636   if (prev) {
2637     error = weed_set_voidptr_value(prev, WEED_LEAF_NEXT, event);
2638     if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2639   }
2640   error = weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
2641   if (error == WEED_ERROR_MEMORY_ALLOCATION) return NULL;
2642   //////////////////////////////////////
2643 
2644   return event_list;
2645 }
2646 
2647 
filter_init_add_pchanges(weed_plant_t * event_list,weed_plant_t * plant,weed_plant_t * init_event,int ntracks,int leave)2648 void **filter_init_add_pchanges(weed_plant_t *event_list, weed_plant_t *plant, weed_plant_t *init_event, int ntracks,
2649                                 int leave) {
2650   // add the initial values for all parameters when we insert a filter_init event
2651   weed_plant_t **in_params = NULL, **in_ptmpls;
2652 
2653   void **pchain = NULL;
2654   void **in_pchanges = NULL;
2655 
2656   weed_plant_t *filter = plant, *in_param;
2657 
2658   weed_timecode_t tc = get_event_timecode(init_event);
2659 
2660   boolean is_inst = FALSE;
2661 
2662   int num_params, i;
2663 
2664   if (WEED_PLANT_IS_FILTER_INSTANCE(plant)) {
2665     filter = weed_instance_get_filter(plant, TRUE);
2666     is_inst = TRUE;
2667   }
2668 
2669   // add param_change events and set "in_params"
2670   if (!weed_get_plantptr_value(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, NULL)) return NULL;
2671 
2672   in_ptmpls = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES, &num_params);
2673 
2674   pchain = (void **)lives_malloc((num_params + 1) * sizeof(void *));
2675   pchain[num_params] = NULL;
2676 
2677   if (!is_inst) in_params = weed_params_create(filter, TRUE);
2678 
2679   if (leave > 0) {
2680     in_pchanges = weed_get_voidptr_array_counted(init_event, WEED_LEAF_IN_PARAMETERS, &num_params);
2681     if (leave > num_params) leave = num_params;
2682   }
2683 
2684   for (i = num_params - 1; i >= 0; i--) {
2685     if (i < leave && in_pchanges && in_pchanges[i]) {
2686       // maintain existing values
2687       pchain[i] = in_pchanges[i];
2688       continue;
2689     }
2690 
2691     pchain[i] = weed_plant_new(WEED_PLANT_EVENT);
2692     weed_set_int_value((weed_plant_t *)pchain[i], WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_PARAM_CHANGE);
2693     weed_set_int64_value((weed_plant_t *)pchain[i], WEED_LEAF_TIMECODE, tc);
2694 
2695     if (!is_inst) in_param = in_params[i];
2696     else in_param = weed_inst_in_param(plant, i, FALSE, FALSE);
2697 
2698     if (is_perchannel_multiw(in_param)) {
2699       // if the parameter is element-per-channel, fill up to number of channels
2700       fill_param_vals_to(in_param, in_ptmpls[i], ntracks - 1);
2701     }
2702 
2703     weed_leaf_dup((weed_plant_t *)pchain[i], in_param, WEED_LEAF_VALUE);
2704 
2705     weed_set_int_value((weed_plant_t *)pchain[i], WEED_LEAF_INDEX, i);
2706     weed_set_voidptr_value((weed_plant_t *)pchain[i], WEED_LEAF_INIT_EVENT, init_event);
2707     weed_set_voidptr_value((weed_plant_t *)pchain[i], WEED_LEAF_NEXT_CHANGE, NULL);
2708     weed_set_voidptr_value((weed_plant_t *)pchain[i], WEED_LEAF_PREV_CHANGE, NULL);
2709     weed_set_boolean_value((weed_plant_t *)pchain[i], WEED_LEAF_IS_DEF_VALUE, WEED_TRUE);
2710     //weed_add_plant_flags((weed_plant_t *)pchain[i], WEED_LEAF_READONLY_PLUGIN);
2711 
2712     insert_param_change_event_at(event_list, init_event, (weed_plant_t *)pchain[i]);
2713   }
2714 
2715   if (in_pchanges) lives_free(in_pchanges);
2716 
2717   if (!is_inst) {
2718     weed_in_params_free(in_params, num_params);
2719     lives_free(in_params);
2720   } else {
2721     lives_free(in_params);
2722   }
2723   lives_free(in_ptmpls);
2724 
2725   weed_set_voidptr_array(init_event, WEED_LEAF_IN_PARAMETERS, num_params, pchain);
2726 
2727   return pchain;
2728 }
2729 
2730 
append_filter_init_event(weed_plant_t * event_list,weed_timecode_t tc,int filter_idx,int num_in_tracks,int key,weed_plant_t * inst)2731 weed_plant_t *append_filter_init_event(weed_plant_t *event_list, weed_timecode_t tc, int filter_idx,
2732                                        int num_in_tracks, int key, weed_plant_t *inst) {
2733   weed_plant_t **ctmpl;
2734   weed_plant_t *event, *prev, *filter, *chan;
2735 
2736   char *tmp;
2737 
2738   int e_in_channels, e_out_channels, e_ins, e_outs;
2739   int total_in_channels = 0;
2740   int total_out_channels = 0;
2741   int my_in_tracks = 0;
2742 
2743   int i;
2744 
2745   if (!event_list) {
2746     event_list = lives_event_list_new(NULL, NULL);
2747     if (!event_list) return NULL;
2748   }
2749 
2750   event = weed_plant_new(WEED_PLANT_EVENT);
2751   weed_set_voidptr_value(event, WEED_LEAF_NEXT, NULL);
2752 
2753   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
2754   weed_set_int_value(event, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_FILTER_INIT);
2755   weed_set_string_value(event, WEED_LEAF_FILTER, (tmp = make_weed_hashname(filter_idx, TRUE, FALSE, 0, FALSE)));
2756   lives_free(tmp);
2757 
2758   filter = get_weed_filter(filter_idx);
2759 
2760   ctmpl = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, &total_in_channels);
2761 
2762   if (total_in_channels > 0) {
2763     int count[total_in_channels];
2764     for (i = 0; i < total_in_channels; i++) {
2765       if (weed_get_boolean_value(ctmpl[i], WEED_LEAF_HOST_DISABLED, NULL) == WEED_FALSE) {
2766         count[i] = 1;
2767         my_in_tracks++;
2768         weed_set_int_value(ctmpl[i], WEED_LEAF_HOST_REPEATS, 1);
2769       } else count[i] = 0;
2770     }
2771 
2772     // TODO ***
2773     if (my_in_tracks < num_in_tracks) {
2774       int repeats;
2775       // we need to use some repeated channels
2776       for (i = 0; i < total_in_channels; i++) {
2777         if (weed_plant_has_leaf(ctmpl[i], WEED_LEAF_MAX_REPEATS) && (count[i] > 0 || has_usable_palette(ctmpl[i]))) {
2778           repeats = weed_get_int_value(ctmpl[i], WEED_LEAF_MAX_REPEATS, NULL);
2779           if (repeats == 0) {
2780             count[i] += num_in_tracks - my_in_tracks;
2781 
2782             /*
2783                   weed_set_int_value(ctmpl[i],WEED_LEAF_HOST_REPEATS,count[i]);
2784                   weed_set_boolean_value(ctmpl[i],WEED_LEAF_HOST_DISABLED,WEED_FALSE);
2785             */
2786 
2787             break;
2788           }
2789           count[i] += num_in_tracks - my_in_tracks >= repeats - 1 ? repeats - 1 : num_in_tracks - my_in_tracks;
2790 
2791           /*
2792                 weed_set_int_value(ctmpl[i],WEED_LEAF_HOST_REPEATS,count[i]);
2793                 weed_set_boolean_value(ctmpl[i],WEED_LEAF_HOST_DISABLED,WEED_FALSE);
2794           */
2795 
2796           my_in_tracks += count[i] - 1;
2797           if (my_in_tracks == num_in_tracks) break;
2798         }
2799       }
2800     }
2801     weed_set_int_array(event, WEED_LEAF_IN_COUNT, total_in_channels, count);
2802     lives_free(ctmpl);
2803   }
2804 
2805   ctmpl = weed_get_plantptr_array_counted(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &total_out_channels);
2806 
2807   if (total_out_channels > 0) {
2808     int count[total_out_channels];
2809     ctmpl = weed_get_plantptr_array(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, NULL);
2810     for (i = 0; i < total_out_channels; i++) {
2811       if (!weed_plant_has_leaf(ctmpl[i], WEED_LEAF_HOST_DISABLED) ||
2812           weed_get_boolean_value(ctmpl[i], WEED_LEAF_HOST_DISABLED, NULL) != WEED_TRUE) count[i] = 1;
2813       else count[i] = 0;
2814     }
2815     lives_free(ctmpl);
2816 
2817     weed_set_int_array(event, WEED_LEAF_OUT_COUNT, total_out_channels, count);
2818   }
2819 
2820   e_ins = e_in_channels = enabled_in_channels(get_weed_filter(filter_idx), FALSE);
2821   e_outs = e_out_channels = enabled_out_channels(get_weed_filter(filter_idx), FALSE);
2822 
2823   // discount alpha_channels (in and out)
2824   if (inst) {
2825     for (i = 0; i < e_ins; i++) {
2826       chan = get_enabled_channel(inst, i, TRUE);
2827       if (weed_palette_is_alpha(weed_layer_get_palette(chan))) e_in_channels--;
2828     }
2829 
2830     // handling for compound fx
2831     while (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE)) inst = weed_get_plantptr_value(inst,
2832           WEED_LEAF_HOST_NEXT_INSTANCE, NULL);
2833 
2834     for (i = 0; i < e_outs; i++) {
2835       chan = get_enabled_channel(inst, i, FALSE);
2836       if (weed_palette_is_alpha(weed_layer_get_palette(chan))) e_out_channels--;
2837     }
2838   }
2839 
2840   // here we map our tracks to channels
2841   if (e_in_channels != 0) {
2842     if (e_in_channels == 1) {
2843       weed_set_int_value(event, WEED_LEAF_IN_TRACKS, 0);
2844     } else {
2845       int *tracks = (int *)lives_malloc(2 * sizint);
2846       tracks[0] = 0;
2847       tracks[1] = 1;
2848       weed_set_int_array(event, WEED_LEAF_IN_TRACKS, 2, tracks);
2849       lives_free(tracks);
2850     }
2851   }
2852 
2853   if (e_out_channels > 0) {
2854     weed_set_int_value(event, WEED_LEAF_OUT_TRACKS, 0);
2855   }
2856 
2857   if (key > -1) {
2858     weed_set_int_value(event, WEED_LEAF_HOST_KEY, key);
2859     weed_set_int_value(event, WEED_LEAF_HOST_MODE, rte_key_getmode(key));
2860   }
2861 
2862   ///////////////////////////////////////////////////////////////////////////////////
2863 #ifdef DEBUG_EVENTS
2864   g_print("adding init event at tc %"PRId64"\n", tc);
2865 #endif
2866 
2867   if (!get_first_event(event_list)) {
2868     weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
2869     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, NULL);
2870   } else {
2871     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, get_last_event(event_list));
2872   }
2873   //weed_add_plant_flags(event, WEED_LEAF_READONLY_PLUGIN);
2874   prev = get_prev_event(event);
2875   if (prev) weed_set_voidptr_value(prev, WEED_LEAF_NEXT, event);
2876   weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
2877 
2878   return event_list;
2879 }
2880 
2881 
append_filter_deinit_event(weed_plant_t * event_list,weed_timecode_t tc,void * init_event,void ** pchain)2882 weed_plant_t *append_filter_deinit_event(weed_plant_t *event_list, weed_timecode_t tc, void *init_event, void **pchain) {
2883   weed_plant_t *event, *prev;
2884 
2885   if (!event_list) {
2886     event_list = lives_event_list_new(NULL, NULL);
2887     if (!event_list) return NULL;
2888   }
2889 
2890   event = weed_plant_new(WEED_PLANT_EVENT);
2891   weed_set_voidptr_value(event, WEED_LEAF_NEXT, NULL);
2892 
2893   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
2894   weed_set_int_value(event, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_FILTER_DEINIT);
2895   weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENT, init_event);
2896   weed_leaf_delete((weed_plant_t *)init_event, WEED_LEAF_DEINIT_EVENT); // delete since we assign a placeholder with int64 type
2897   weed_set_plantptr_value(init_event, WEED_LEAF_DEINIT_EVENT, (void *)event);
2898   if (pchain) {
2899     int num_params = 0;
2900     while (pchain[num_params]) num_params++;
2901     weed_set_voidptr_array(event, WEED_LEAF_IN_PARAMETERS, num_params, pchain);
2902   }
2903 
2904   if (!get_first_event(event_list)) {
2905     weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
2906     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, NULL);
2907   } else {
2908     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, get_last_event(event_list));
2909   }
2910   //weed_add_plant_flags(event, WEED_LEAF_READONLY_PLUGIN);
2911   prev = get_prev_event(event);
2912   if (prev) weed_set_voidptr_value(prev, WEED_LEAF_NEXT, event);
2913   weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
2914 
2915   return event_list;
2916 }
2917 
2918 
append_param_change_event(weed_plant_t * event_list,weed_timecode_t tc,int pnum,weed_plant_t * param,void * init_event,void ** pchain)2919 weed_plant_t *append_param_change_event(weed_plant_t *event_list, weed_timecode_t tc, int pnum,
2920                                         weed_plant_t *param, void *init_event, void **pchain) {
2921   weed_plant_t *event, *prev, *xevent;
2922   weed_plant_t *last_pchange_event;
2923 
2924   if (!event_list) {
2925     event_list = lives_event_list_new(NULL, NULL);
2926     if (!event_list) return NULL;
2927   }
2928 
2929   event = weed_plant_new(WEED_PLANT_EVENT);
2930   weed_set_voidptr_value(event, WEED_LEAF_NEXT, NULL);
2931 
2932   // TODO - error check
2933   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
2934   weed_set_int_value(event, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_PARAM_CHANGE);
2935   weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENT, init_event);
2936   weed_set_int_value(event, WEED_LEAF_INDEX, pnum);
2937   weed_leaf_copy(event, WEED_LEAF_VALUE, param, WEED_LEAF_VALUE);
2938 
2939   last_pchange_event = (weed_plant_t *)pchain[pnum];
2940   while ((xevent = (weed_plant_t *)weed_get_voidptr_value(last_pchange_event, WEED_LEAF_NEXT_CHANGE, NULL)) != NULL)
2941     last_pchange_event = xevent;
2942 
2943   if (weed_event_get_timecode(last_pchange_event) == tc && !is_init_pchange(init_event, last_pchange_event)) {
2944     weed_event_t *dup_event = last_pchange_event;
2945     last_pchange_event = (weed_plant_t *)weed_get_voidptr_value(last_pchange_event, WEED_LEAF_PREV_CHANGE, NULL);
2946     delete_event(event_list, dup_event);
2947   }
2948 
2949   weed_set_voidptr_value(last_pchange_event, WEED_LEAF_NEXT_CHANGE, event);
2950   weed_set_voidptr_value(event, WEED_LEAF_PREV_CHANGE, last_pchange_event);
2951   weed_set_voidptr_value(event, WEED_LEAF_NEXT_CHANGE, NULL);
2952 
2953   if (!get_first_event(event_list)) {
2954     weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
2955     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, NULL);
2956   } else {
2957     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, get_last_event(event_list));
2958   }
2959   //weed_add_plant_flags(event, WEED_LEAF_READONLY_PLUGIN);
2960   prev = get_prev_event(event);
2961   if (prev) weed_set_voidptr_value(prev, WEED_LEAF_NEXT, event);
2962   weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
2963 
2964   return event_list;
2965 }
2966 
2967 
append_filter_map_event(weed_plant_t * event_list,weed_timecode_t tc,void ** init_events)2968 weed_plant_t *append_filter_map_event(weed_plant_t *event_list, weed_timecode_t tc, void **init_events) {
2969   weed_plant_t *event, *prev;
2970   int i = 0;
2971 
2972   if (!event_list) {
2973     event_list = lives_event_list_new(NULL, NULL);
2974     if (!event_list) return NULL;
2975   }
2976 
2977   event = weed_plant_new(WEED_PLANT_EVENT);
2978   weed_set_voidptr_value(event, WEED_LEAF_NEXT, NULL);
2979 
2980   // TODO - error check
2981   weed_set_int64_value(event, WEED_LEAF_TIMECODE, tc);
2982   weed_set_int_value(event, WEED_LEAF_EVENT_TYPE, WEED_EVENT_TYPE_FILTER_MAP);
2983 
2984   if (init_events) for (i = 0; init_events[i]; i++);
2985 
2986   if (i == 0) weed_set_voidptr_value(event, WEED_LEAF_INIT_EVENTS, NULL);
2987   else weed_set_voidptr_array(event, WEED_LEAF_INIT_EVENTS, i, init_events);
2988 
2989 #ifdef DEBUG_EVENTS
2990   g_print("adding map event %p at tc %"PRId64"\n", init_events[0], tc);
2991 #endif
2992 
2993   if (!get_first_event(event_list)) {
2994     weed_set_voidptr_value(event_list, WEED_LEAF_FIRST, event);
2995     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, NULL);
2996   } else {
2997     weed_set_voidptr_value(event, WEED_LEAF_PREVIOUS, get_last_event(event_list));
2998   }
2999   //weed_add_plant_flags(event, WEED_LEAF_READONLY_PLUGIN);
3000   prev = get_prev_event(event);
3001   if (prev) weed_set_voidptr_value(prev, WEED_LEAF_NEXT, event);
3002   weed_set_voidptr_value(event_list, WEED_LEAF_LAST, event);
3003 
3004   return event_list;
3005 }
3006 
3007 
get_active_track_list(int * clip_index,int num_tracks,weed_plant_t * filter_map)3008 void get_active_track_list(int *clip_index, int num_tracks, weed_plant_t *filter_map) {
3009   // replace entries in clip_index with 0 if the track is not either the front track or an input to a filter
3010 
3011   // TODO *** we should ignore any filter which does not eventually output to the front track,
3012   // this involves examining the filter map in reverse order and mapping out_tracks back to in_tracks
3013   // marking those which we cover
3014 
3015   weed_plant_t **init_events;
3016   weed_plant_t *filter;
3017 
3018   char *filter_hash;
3019 
3020   int *in_tracks, *out_tracks;
3021   int ninits, nintracks, nouttracks;
3022   int idx;
3023   int front = -1;
3024 
3025   register int i, j;
3026 
3027   /// if we are previewing a solo effect in multitrack, then we ignore the usual rules for the front frame, and instead
3028   /// use the (first) output track from the filter instance
3029   if (mainw->multitrack && mainw->multitrack->solo_inst && mainw->multitrack->init_event && !LIVES_IS_PLAYING) {
3030     weed_event_t *ievent = mainw->multitrack->init_event;
3031     front = weed_get_int_value(ievent, WEED_LEAF_OUT_TRACKS, NULL);
3032   }
3033 
3034   for (i = 0; i < num_tracks; i++) {
3035     if ((front == -1 || front == i) && clip_index[i] > 0) {
3036       mainw->active_track_list[i] = clip_index[i];
3037       front = i;
3038     } else mainw->active_track_list[i] = 0;
3039   }
3040 
3041   if (!filter_map || !weed_plant_has_leaf(filter_map, WEED_LEAF_INIT_EVENTS)) return;
3042   init_events = (weed_plant_t **)weed_get_voidptr_array_counted(filter_map, WEED_LEAF_INIT_EVENTS, &ninits);
3043   if (!init_events) return;
3044 
3045   for (i = ninits - 1; i >= 0; i--) {
3046     // get the filter and make sure it has video chans out, which feed to an active track
3047     if (!weed_plant_has_leaf(init_events[i], WEED_LEAF_OUT_TRACKS)
3048         || !weed_plant_has_leaf(init_events[i], WEED_LEAF_IN_TRACKS)) continue;
3049     if (mainw->multitrack && mainw->multitrack->solo_inst && mainw->multitrack->init_event
3050         && mainw->multitrack->init_event != init_events[i] && !LIVES_IS_PLAYING) continue;
3051     filter_hash = weed_get_string_value(init_events[i], WEED_LEAF_FILTER, NULL);
3052     if ((idx = weed_get_idx_for_hashname(filter_hash, TRUE)) != -1) {
3053       filter = get_weed_filter(idx);
3054       if (has_video_chans_in(filter, FALSE) && has_video_chans_out(filter, FALSE)) {
3055         boolean is_valid = FALSE;
3056         out_tracks = weed_get_int_array_counted(init_events[i], WEED_LEAF_OUT_TRACKS, &nouttracks);
3057         for (j = 0; j < nouttracks; j++) {
3058           if (j >= mainw->num_tracks) break;
3059           if (mainw->active_track_list[out_tracks[j]] != 0) {
3060             is_valid = TRUE;
3061             break;
3062           }
3063         }
3064         lives_free(out_tracks);
3065         if (is_valid) {
3066           in_tracks = weed_get_int_array_counted(init_events[i], WEED_LEAF_IN_TRACKS, &nintracks);
3067           for (j = 0; j < nintracks; j++) {
3068             if (j >= mainw->num_tracks) break;
3069             mainw->active_track_list[in_tracks[j]] = clip_index[in_tracks[j]];
3070           }
3071           lives_free(in_tracks);
3072         }
3073       }
3074     }
3075     lives_free(filter_hash);
3076   }
3077 
3078   lives_free(init_events);
3079 }
3080 
3081 
process_events(weed_plant_t * next_event,boolean process_audio,weed_timecode_t curr_tc)3082 weed_plant_t *process_events(weed_plant_t *next_event, boolean process_audio, weed_timecode_t curr_tc) {
3083   // here we play back (preview) with an event_list
3084   // we process all events, but drop frames (unless mainw->nodrop is set)
3085 
3086   static weed_timecode_t aseek_tc = 0;
3087   weed_timecode_t tc, next_tc;
3088 
3089   static double stored_avel = 0.;
3090 
3091   static int dframes = 0, spare_cycles = 0;
3092 
3093   int *in_count = NULL;
3094 
3095   void *init_event;
3096 
3097   weed_plant_t *next_frame_event, *return_event;
3098   weed_plant_t *filter;
3099   weed_plant_t *inst, *orig_inst;
3100 
3101   weed_plant_t **citmpl = NULL, **cotmpl = NULL;
3102   weed_plant_t **bitmpl = NULL, **botmpl = NULL;
3103   weed_plant_t **source_params, **in_params;
3104 
3105   char *filter_name;
3106   char *key_string;
3107 
3108   int current_file;
3109 
3110   int num_params, offset = 0;
3111   int num_in_count = 0;
3112   int num_in_channels = 0, num_out_channels = 0;
3113   int new_file;
3114   int etype;
3115   int key, idx;
3116   int easing;
3117 
3118   int i;
3119 
3120   if (!next_event) {
3121     aseek_tc = 0;
3122     dframes = 0;
3123     return NULL;
3124   }
3125 
3126   tc = get_event_timecode(next_event);
3127 
3128   if (mainw->playing_file != -1 && tc > curr_tc) {
3129     // next event is in our future
3130     if (mainw->multitrack && mainw->last_display_ticks > 0) {
3131       if ((mainw->fixed_fpsd > 0. && (curr_tc - mainw->last_display_ticks) / TICKS_PER_SECOND_DBL >= 1. / mainw->fixed_fpsd) ||
3132           (mainw->vpp && mainw->vpp->fixed_fpsd > 0. && mainw->ext_playback &&
3133            (curr_tc - mainw->last_display_ticks) / TICKS_PER_SECOND_DBL >= 1. / mainw->vpp->fixed_fpsd)) {
3134         // ...but playing at fixed fps, which is faster than mt fps
3135         mainw->pchains = pchains;
3136         if (prefs->pbq_adaptive) {
3137           if (dframes > 0) update_effort(dframes, TRUE);
3138           else update_effort(spare_cycles, FALSE);
3139           dframes = 0;
3140           spare_cycles = 0;
3141         }
3142         load_frame_image(cfile->last_frameno >= 1 ? cfile->last_frameno : cfile->start);
3143         if (prefs->show_player_stats) {
3144           mainw->fps_measure++;
3145         }
3146         if (mainw->last_display_ticks == 0) mainw->last_display_ticks = curr_tc;
3147         else {
3148           if (mainw->vpp && mainw->ext_playback && mainw->vpp->fixed_fpsd > 0.)
3149             mainw->last_display_ticks += TICKS_PER_SECOND_DBL / mainw->vpp->fixed_fpsd;
3150           else if (mainw->fixed_fpsd > 0.)
3151             mainw->last_display_ticks += TICKS_PER_SECOND_DBL / mainw->fixed_fpsd;
3152           else mainw->last_display_ticks = curr_tc;
3153         }
3154         mainw->pchains = NULL;
3155       } else spare_cycles++;
3156     }
3157     return next_event;
3158   }
3159 
3160   if (mainw->cevent_tc != -1)
3161     aseek_tc += (weed_timecode_t)((double)(tc - mainw->cevent_tc) * stored_avel);
3162   mainw->cevent_tc = tc;
3163 
3164   return_event = get_next_event(next_event);
3165   etype = get_event_type(next_event);
3166   switch (etype) {
3167   case WEED_EVENT_TYPE_FRAME:
3168 
3169 #ifdef DEBUG_EVENTS
3170     g_print("event: frame event at tc %"PRId64" curr_tc=%"PRId64"\n", tc, curr_tc);
3171 #endif
3172 
3173     if (!mainw->multitrack && is_realtime_aplayer(prefs->audio_player) && WEED_EVENT_IS_AUDIO_FRAME(next_event)) {
3174       // keep track of current seek position, for animating playback pointers
3175       int *aclips = weed_get_int_array(next_event, WEED_LEAF_AUDIO_CLIPS, NULL);
3176       double *aseeks = weed_get_double_array(next_event, WEED_LEAF_AUDIO_SEEKS, NULL);
3177 
3178       if (aclips[1] > 0) {
3179         aseek_tc = aseeks[0] * TICKS_PER_SECOND_DBL;
3180         stored_avel = aseeks[1];
3181       }
3182 
3183       lives_freep((void **)&aseeks);
3184       lives_freep((void **)&aclips);
3185     }
3186 
3187     if ((next_frame_event = get_next_frame_event(next_event))) {
3188       next_tc = get_event_timecode(next_frame_event);
3189       // drop frame if it is too far behind
3190       if (LIVES_IS_PLAYING && next_tc <= curr_tc) {
3191         if (prefs->pbq_adaptive) dframes++;
3192         if (!prefs->noframedrop) break;
3193       }
3194       if (!mainw->fs && !prefs->hide_framebar && !mainw->multitrack) {
3195         lives_signal_handler_block(mainw->spinbutton_pb_fps, mainw->pb_fps_func);
3196         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), cfile->pb_fps);
3197         lives_signal_handler_unblock(mainw->spinbutton_pb_fps, mainw->pb_fps_func);
3198       }
3199     }
3200 
3201     lives_freep((void **)&mainw->clip_index);
3202     lives_freep((void **)&mainw->frame_index);
3203 
3204     mainw->clip_index = weed_get_int_array_counted(next_event, WEED_LEAF_CLIPS, &mainw->num_tracks);
3205     mainw->frame_index = weed_get_int64_array(next_event, WEED_LEAF_FRAMES, NULL);
3206 
3207     if (mainw->scrap_file != -1) {
3208       int nclips = mainw->num_tracks;
3209       for (i = 0; i < nclips; i++) {
3210         if (mainw->clip_index[i] == mainw->scrap_file) {
3211           int64_t offs = weed_get_int64_value(next_event, WEED_LEAF_HOST_SCRAP_FILE_OFFSET, NULL);
3212           if (!mainw->files[mainw->scrap_file]->ext_src) load_from_scrap_file(NULL, -1);
3213           lives_lseek_buffered_rdonly_absolute(LIVES_POINTER_TO_INT(mainw->files[mainw->scrap_file]->ext_src), offs);
3214         }
3215       }
3216     }
3217 
3218     // if we are in multitrack mode, we will just set up NULL layers and let the effects pull our frames
3219     if (mainw->multitrack) {
3220 
3221       if (!LIVES_IS_PLAYING || ((mainw->fixed_fpsd <= 0. && (!mainw->vpp || mainw->vpp->fixed_fpsd <= 0. || !mainw->ext_playback))
3222                                 || (mainw->fixed_fpsd > 0. && (curr_tc - mainw->last_display_ticks) / TICKS_PER_SECOND_DBL >= 1. / mainw->fixed_fpsd) ||
3223                                 (mainw->vpp && mainw->vpp->fixed_fpsd > 0. && mainw->ext_playback &&
3224                                  (curr_tc - mainw->last_display_ticks) / TICKS_PER_SECOND_DBL >= 1. / mainw->vpp->fixed_fpsd))) {
3225         mainw->pchains = pchains;
3226 
3227         if (LIVES_IS_PLAYING) {
3228           if (prefs->pbq_adaptive) {
3229             update_effort(dframes, TRUE);
3230             dframes = 0;
3231             spare_cycles = 0;
3232           }
3233         }
3234 
3235         load_frame_image(cfile->frameno);
3236 
3237         if (LIVES_IS_PLAYING) {
3238           if (prefs->show_player_stats) {
3239             mainw->fps_measure++;
3240           }
3241           if (mainw->last_display_ticks == 0) mainw->last_display_ticks = curr_tc;
3242           else {
3243             if (mainw->vpp && mainw->ext_playback && mainw->vpp->fixed_fpsd > 0.)
3244               mainw->last_display_ticks += TICKS_PER_SECOND_DBL / mainw->vpp->fixed_fpsd;
3245             else if (mainw->fixed_fpsd > 0.)
3246               mainw->last_display_ticks += TICKS_PER_SECOND_DBL / mainw->fixed_fpsd;
3247             else mainw->last_display_ticks = curr_tc;
3248           }
3249         }
3250         mainw->pchains = NULL;
3251       } else spare_cycles++;
3252     } else {
3253       if (mainw->num_tracks > 1) {
3254         mainw->blend_file = mainw->clip_index[1];
3255         if (mainw->blend_file > -1) mainw->files[mainw->blend_file]->frameno = mainw->frame_index[1];
3256       } else mainw->blend_file = -1;
3257 
3258       new_file = -1;
3259       for (i = 0; i < mainw->num_tracks && new_file == -1; i++) {
3260         new_file = mainw->clip_index[i];
3261       }
3262       if (i == 2) mainw->blend_file = -1;
3263 
3264 #ifdef DEBUG_EVENTS
3265       g_print("event: front frame is %d tc %"PRId64" curr_tc=%"PRId64"\n", mainw->frame_index[0], tc, curr_tc);
3266 #endif
3267       if ((inst = weed_get_plantptr_value(next_event, WEED_LEAF_HOST_EASING_END, NULL))) {
3268         easing = weed_get_int_value(next_event, WEED_LEAF_EASE_OUT, NULL);
3269         //if (weed_get_int_value(inst, WEED_LEAF_EASE_OUT_FRAMES, NULL) > 0) {
3270         weed_set_int_value(inst, WEED_LEAF_EASE_OUT, easing);
3271         weed_set_boolean_value(inst, WEED_LEAF_AUTO_EASING, WEED_TRUE);
3272         //}
3273       }
3274 
3275       // handle case where new_file==-1: we must somehow create a blank frame in load_frame_image
3276       if (new_file == -1) new_file = mainw->current_file;
3277       if (prefs->pbq_adaptive) {
3278         if (dframes > 0) update_effort(dframes, TRUE);
3279         else update_effort(spare_cycles, FALSE);
3280         dframes = 0;
3281       }
3282       if (!mainw->urgency_msg && weed_plant_has_leaf(next_event, WEED_LEAF_OVERLAY_TEXT)) {
3283         mainw->urgency_msg = weed_get_string_value(next_event, WEED_LEAF_OVERLAY_TEXT, NULL);
3284       }
3285 
3286       if (new_file != mainw->current_file) {
3287         mainw->files[new_file]->frameno = mainw->frame_index[i - 1];
3288         if (new_file != mainw->scrap_file) {
3289           // switch to a new file
3290           do_quick_switch(new_file);
3291           cfile->next_event = return_event;
3292           return_event = NULL;
3293         } else {
3294           /// load a frame from the scrap file
3295           mainw->files[new_file]->hsize = cfile->hsize; // set size of scrap file
3296           mainw->files[new_file]->vsize = cfile->vsize;
3297           current_file = mainw->current_file;
3298           mainw->current_file = new_file;
3299           mainw->aframeno = (double)(aseek_tc / TICKS_PER_SECOND_DBL) * cfile->fps;
3300           mainw->pchains = pchains;
3301           load_frame_image(cfile->frameno);
3302           if (prefs->show_player_stats) {
3303             mainw->fps_measure++;
3304           }
3305           mainw->pchains = NULL;
3306           mainw->current_file = current_file;
3307         }
3308         break;
3309       } else {
3310         cfile->frameno = mainw->frame_index[i - 1];
3311         mainw->aframeno = (double)(aseek_tc / TICKS_PER_SECOND_DBL) * cfile->fps;
3312         mainw->pchains = pchains;
3313         load_frame_image(cfile->frameno);
3314         mainw->pchains = NULL;
3315       }
3316     }
3317     cfile->next_event = get_next_event(next_event);
3318     break;
3319   case WEED_EVENT_TYPE_FILTER_INIT:
3320     // effect init
3321     //  bind the weed_fx to next free key/0
3322     filter_name = weed_get_string_value(next_event, WEED_LEAF_FILTER, NULL);
3323     idx = weed_get_idx_for_hashname(filter_name, TRUE);
3324     lives_free(filter_name);
3325 
3326     if (idx != -1) {
3327       filter = get_weed_filter(idx);
3328 
3329       if (!process_audio && is_pure_audio(filter, FALSE)) {
3330         if (weed_plant_has_leaf(next_event, WEED_LEAF_HOST_TAG)) weed_leaf_delete(next_event, WEED_LEAF_HOST_TAG);
3331         break; // audio effects are processed in the audio renderer
3332       }
3333 
3334       if (process_audio && !is_pure_audio(filter, FALSE)) break;
3335 
3336       key = get_next_free_key();
3337       weed_add_effectkey_by_idx(key + 1, idx);
3338       key_string = lives_strdup_printf("%d", key);
3339       weed_set_string_value(next_event, WEED_LEAF_HOST_TAG, key_string);
3340       lives_free(key_string);
3341 
3342 #ifdef DEBUG_EVENTS
3343       g_print("event: init effect on key %d at tc %"PRId64" curr_tc=%"PRId64"\n", key, tc, curr_tc);
3344 #endif
3345       if (weed_plant_has_leaf(next_event, WEED_LEAF_IN_COUNT)) {
3346         in_count = weed_get_int_array_counted(next_event, WEED_LEAF_IN_COUNT, &num_in_count);
3347       }
3348 
3349       citmpl = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, &num_in_channels);
3350       if (num_in_channels > 0) {
3351         bitmpl = (weed_plant_t **)lives_malloc(num_in_channels * sizeof(weed_plant_t *));
3352         if (num_in_channels != num_in_count) LIVES_ERROR("num_in_count != num_in_channels");
3353         for (i = 0; i < num_in_channels; i++) {
3354           bitmpl[i] = weed_plant_copy(citmpl[i]);
3355           if (in_count[i] > 0) {
3356             weed_set_boolean_value(citmpl[i], WEED_LEAF_HOST_DISABLED, WEED_FALSE);
3357             weed_set_int_value(citmpl[i], WEED_LEAF_HOST_REPEATS, in_count[i]);
3358           } else weed_set_boolean_value(citmpl[i], WEED_LEAF_HOST_DISABLED, WEED_TRUE);
3359         }
3360       }
3361 
3362       lives_freep((void **)&in_count);
3363 
3364       cotmpl = weed_get_plantptr_array_counted(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &num_out_channels);
3365       if (num_out_channels > 0) {
3366         botmpl = (weed_plant_t **)lives_malloc(num_out_channels * sizeof(weed_plant_t *));
3367         for (i = 0; i < num_out_channels; i++) {
3368           botmpl[i] = weed_plant_copy(cotmpl[i]);
3369         }
3370       }
3371 
3372       weed_init_effect(key);
3373 
3374       // restore channel state / number from backup
3375 
3376       if (num_in_channels > 0) {
3377         for (i = 0; i < num_in_channels; i++) {
3378           weed_leaf_copy_or_delete(citmpl[i], WEED_LEAF_HOST_DISABLED, bitmpl[i]);
3379           weed_leaf_copy_or_delete(citmpl[i], WEED_LEAF_HOST_REPEATS, bitmpl[i]);
3380           weed_plant_free(bitmpl[i]);
3381         }
3382         lives_free(bitmpl);
3383         lives_free(citmpl);
3384       }
3385 
3386       if (num_out_channels > 0) {
3387         for (i = 0; i < num_out_channels; i++) {
3388           weed_leaf_copy_or_delete(cotmpl[i], WEED_LEAF_HOST_DISABLED, botmpl[i]);
3389           weed_leaf_copy_or_delete(cotmpl[i], WEED_LEAF_HOST_REPEATS, botmpl[i]);
3390           weed_plant_free(botmpl[i]);
3391         }
3392         lives_free(botmpl);
3393         lives_free(cotmpl);
3394       }
3395 
3396       // reinit effect with saved parameters
3397       orig_inst = inst = rte_keymode_get_instance(key + 1, 0);
3398 
3399       if (weed_plant_has_leaf(next_event, WEED_LEAF_IN_PARAMETERS)) {
3400         int nparams;
3401         void **xpchains = weed_get_voidptr_array_counted(next_event, WEED_LEAF_IN_PARAMETERS, &nparams);
3402         pchains[key] = (void **)lives_realloc(pchains[key], (nparams + 1) * sizeof(void *));
3403         for (i = 0; i < nparams; i++) pchains[key][i] = xpchains[i];
3404         pchains[key][nparams] = NULL;
3405         lives_free(xpchains);
3406       } else pchains[key] = NULL;
3407 
3408 filterinit1:
3409 
3410       num_params = num_in_params(inst, FALSE, FALSE);
3411 
3412       if (num_params > 0) {
3413         weed_call_deinit_func(inst);
3414         if (weed_plant_has_leaf(next_event, WEED_LEAF_IN_PARAMETERS)) {
3415           in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
3416           source_params = (weed_plant_t **)pchains[key];
3417 
3418           for (i = 0; i < num_params; i++) {
3419             if (source_params && source_params[i + offset] && is_init_pchange(next_event, source_params[i + offset]))
3420               weed_leaf_dup(in_params[i], source_params[i + offset], WEED_LEAF_VALUE);
3421           }
3422           lives_free(in_params);
3423         }
3424 
3425         offset += num_params;
3426 
3427         filter = weed_instance_get_filter(inst, FALSE);
3428 
3429         if (weed_plant_has_leaf(filter, WEED_LEAF_INIT_FUNC)) {
3430           weed_init_f init_func = (weed_init_f)weed_get_funcptr_value(filter, WEED_LEAF_INIT_FUNC, NULL);
3431           if (init_func) {
3432             char *cwd = cd_to_plugin_dir(filter);
3433             (*init_func)(inst);
3434             lives_chdir(cwd, FALSE);
3435             lives_free(cwd);
3436           }
3437         }
3438 
3439         weed_set_boolean_value(inst, WEED_LEAF_HOST_INITED, WEED_TRUE);
3440         weed_set_boolean_value(inst, WEED_LEAF_HOST_UNUSED, WEED_TRUE);
3441       }
3442 
3443       if (weed_plant_has_leaf(next_event, WEED_LEAF_HOST_KEY)) {
3444         // mt events will not have this;
3445         // it is used to connect params and alpha channels during rendering
3446         // holds our original key/mode values
3447 
3448         int hostkey = weed_get_int_value(next_event, WEED_LEAF_HOST_KEY, NULL);
3449         int hostmode = weed_get_int_value(next_event, WEED_LEAF_HOST_MODE, NULL);
3450 
3451         weed_set_int_value(inst, WEED_LEAF_HOST_KEY, hostkey);
3452         weed_set_int_value(inst, WEED_LEAF_HOST_MODE, hostmode);
3453 
3454         if ((easing = weed_get_int_value(next_event, WEED_LEAF_EASE_OUT, NULL)) > 0) {
3455           g_print("precev found easing %d on %p\n", easing, next_event);
3456           weed_plant_t *deinit = weed_get_plantptr_value(next_event, WEED_LEAF_DEINIT_EVENT, NULL);
3457           if (deinit) {
3458             weed_plant_t *event = deinit;
3459             for (i = 0; i < easing && event; i++) {
3460               event = get_prev_frame_event(event);
3461             }
3462             if (event != deinit && event) {
3463               weed_set_int_value(event, WEED_LEAF_EASE_OUT, easing);
3464               weed_set_plantptr_value(event, WEED_LEAF_HOST_EASING_END, inst);
3465 	    // *INDENT-OFF*
3466             }}}}
3467       // *INDENT-ON*
3468 
3469       if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE)) {
3470         // handle compound fx
3471         inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, NULL);
3472         goto filterinit1;
3473       }
3474       weed_instance_unref(orig_inst);
3475     }
3476     break;
3477 
3478   case WEED_EVENT_TYPE_FILTER_DEINIT:
3479     init_event = weed_get_voidptr_value((weed_plant_t *)next_event, WEED_LEAF_INIT_EVENT, NULL);
3480     if (weed_plant_has_leaf((weed_plant_t *)init_event, WEED_LEAF_HOST_TAG)) {
3481       key_string = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_HOST_TAG, NULL);
3482       key = atoi(key_string);
3483       lives_free(key_string);
3484 
3485       filter_name = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_FILTER, NULL);
3486       idx = weed_get_idx_for_hashname(filter_name, TRUE);
3487       lives_free(filter_name);
3488 
3489       filter = get_weed_filter(idx);
3490 
3491       if (!process_audio) {
3492         if (is_pure_audio(filter, FALSE)) break; // audio effects are processed in the audio renderer
3493       }
3494 
3495       if (process_audio && !is_pure_audio(filter, FALSE)) break;
3496 
3497       if ((inst = rte_keymode_get_instance(key + 1, 0))) {
3498         //weed_deinit_effect(key);
3499         weed_delete_effectkey(key + 1, 0);
3500         weed_instance_unref(inst);
3501       }
3502       // no freep !
3503       if (pchains[key]) lives_free(pchains[key]);
3504       pchains[key] = NULL;
3505     }
3506     break;
3507 
3508   case WEED_EVENT_TYPE_FILTER_MAP:
3509     mainw->filter_map = next_event;
3510 #ifdef DEBUG_EVENTS
3511     g_print("got new effect map\n");
3512 #endif
3513     break;
3514   case WEED_EVENT_TYPE_PARAM_CHANGE:
3515     if (!mainw->multitrack) {
3516       init_event = weed_get_voidptr_value((weed_plant_t *)next_event, WEED_LEAF_INIT_EVENT, NULL);
3517       if (weed_plant_has_leaf((weed_plant_t *)init_event, WEED_LEAF_HOST_TAG)) {
3518         key_string = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_HOST_TAG, NULL);
3519         key = atoi(key_string);
3520         lives_free(key_string);
3521 
3522         filter_name = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_FILTER, NULL);
3523         idx = weed_get_idx_for_hashname(filter_name, TRUE);
3524         lives_free(filter_name);
3525 
3526         filter = get_weed_filter(idx);
3527 
3528         if (!process_audio) {
3529           if (is_pure_audio(filter, FALSE)) break; // audio effects are processed in the audio renderer
3530         }
3531 
3532         if (process_audio && !is_pure_audio(filter, FALSE)) break;
3533 
3534         if ((inst = rte_keymode_get_instance(key + 1, 0))) {
3535           int pnum = weed_get_int_value(next_event, WEED_LEAF_INDEX, NULL);
3536           weed_plant_t *param = weed_inst_in_param(inst, pnum, FALSE, FALSE);
3537           weed_leaf_dup(param, next_event, WEED_LEAF_VALUE);
3538         }
3539       }
3540     }
3541     break;
3542   }
3543   return return_event;
3544 }
3545 
3546 
set_proc_label(xprocess * proc,const char * label,boolean copy_old)3547 static char *set_proc_label(xprocess * proc, const char *label, boolean copy_old) {
3548   char *blabel = NULL;
3549   if (!proc) return NULL;
3550   if (copy_old) blabel = lives_strdup(lives_label_get_text(LIVES_LABEL(proc->label)));
3551   lives_label_set_text(LIVES_LABEL(proc->label), label);
3552   lives_widget_queue_draw(proc->processing);
3553   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
3554   return blabel;
3555 }
3556 
3557 
3558 /**
3559    @brief render mainw->event_list to a clip
3560 
3561    the first time, this should be called with reset = TRUE, then always with reset = FALSE for the rest of the rendering
3562    each subsequent call will handle one event in the event list (the current event is stored internally and updated on each call).
3563 
3564    Video and audio are rendered separately, audio is rendered only when:
3565    - a new "audio_frame" is reached
3566    - the end of the list is reached
3567    - or mainw->flush_audio_tc is set to non-zero,
3568 
3569    The first two cases happen automatically, the latter case may be used if a preview is required, to ensure that audio is rendered
3570    up to the preview end point.
3571 
3572    the resulting output may be to an existing clip or a new one. When rendering to an existing clip (cfile->old_frames > 0)
3573    each call may overwrite an existing frame or extend it by (at most) one frame (measured in ouput fps). On this case rec_tc_delta
3574    is used to offset input timecodes to output. When outputting to a new clip, rec_tc_delta is ignored and each frame is appended ar
3575    the next frame timecode
3576 
3577    The event_list is resampled in place, so that out frames coincide with the ouput frame rate (frames may be dropped or duplicated)
3578    - generally the list would have been pre-quantised using quantise_events() so that this process is smoothed out. In multitrack, in and
3579    out frame rates always match so this is not a concern there.
3580 
3581    When rendering to a new clip, the frame width, height, fps and audio values (channels, rate etc) (if rendering audio)
3582    must be set first.
3583 
3584    the event_list is rendered as-is, with the exception that param changes are not applied if rendering in multitrack, since these are
3585    interpolated separately. Otherwise, effect inits / deinits, param changes and filter maps are all updated. Seperate filter map pointers
3586    are maintained for video and audio and only filters for that stream are updated.
3587 
3588    When rendering audio it is most efficient to render
3589    as large blocks as possible. These will be broken into smaller chunks internally and the audio filters are applied and updated using
3590    these smaller chunks. If rendering audio, the final call should be with mainw->flush_audio_tc set to
3591    the event_list length + (1. / cfile->fps) * TICKS_PER_SECOND.
3592    If audio ends before this then it will padded to the end with silence.
3593    When rendering to an existing clip, the behaviour of audio rendering is undefined.
3594 
3595    The following values (lives_render_error_t) may be returned:
3596    LIVES_RENDER_READY - returned after the initial call with reset set to TRUE
3597 
3598    LIVES_RENDER_PROCESSING - the next video event / audio block was handled successfully
3599 
3600    LIVES_RENDER_EFFECTS_PAUSED - processing is paused, the internal state is unchanged
3601 
3602    LIVES_RENDER_COMPLETE - the final event in the list was reached
3603 
3604    LIVES_RENDER_ERROR_WRITE_FRAME - the output frame could not be written, and the user declined to retr
3605 
3606    LIVES_RENDER_ERROR_WRITE_AUDIO
3607    LIVES_RENDER_ERROR_READ_AUDIO
3608 */
render_events(boolean reset,boolean rend_video,boolean rend_audio)3609 lives_render_error_t render_events(boolean reset, boolean rend_video, boolean rend_audio) {
3610 #define SAVE_THREAD
3611 #ifdef SAVE_THREAD
3612   static savethread_priv_t *saveargs = NULL;
3613   static lives_thread_t *saver_thread = NULL;
3614 #else
3615   char oname[PATH_MAX];
3616   char *tmp;
3617   LiVESError *error;
3618 #endif
3619   static weed_timecode_t rec_delta_tc, atc;
3620   static weed_plant_t *event, *eventnext;
3621   static boolean r_audio, r_video;
3622 
3623   weed_timecode_t tc, next_out_tc = 0l, out_tc, dtc = atc;
3624   void *init_event;
3625 
3626   LiVESPixbuf *pixbuf = NULL;
3627 
3628   weed_plant_t *filter;
3629   weed_plant_t **citmpl = NULL, **cotmpl = NULL;
3630   weed_plant_t **bitmpl = NULL, **botmpl = NULL;
3631   weed_plant_t *inst, *orig_inst;
3632   weed_plant_t *next_frame_event = NULL;
3633 
3634   int *in_count = NULL;
3635 
3636   weed_plant_t **source_params, **in_params;
3637   weed_plant_t **layers, *layer = NULL;
3638 
3639   weed_error_t weed_error;
3640   LiVESResponseType retval;
3641 
3642   int key, idx;
3643   int etype;
3644   int layer_palette;
3645   int num_params, offset = 0;
3646   int num_in_count = 0;
3647   int num_in_channels = 0, num_out_channels = 0;
3648   int mytrack;
3649   int scrap_track = -1;
3650   int easing;
3651 
3652   static int progress;
3653   static int xaclips[MAX_AUDIO_TRACKS];
3654   static int out_frame;
3655   static int frame;
3656   static int64_t old_scrap_frame;
3657   static int natracks, nbtracks;
3658   int blend_file = mainw->blend_file;
3659 
3660   boolean is_blank = TRUE;
3661   boolean completed = FALSE;
3662 
3663   static double chvols[MAX_AUDIO_TRACKS];
3664   static double xaseek[MAX_AUDIO_TRACKS], xavel[MAX_AUDIO_TRACKS], atime;
3665 
3666 #ifdef VFADE_RENDER
3667   static weed_timecode_t vfade_in_end;
3668   static weed_timecode_t vfade_out_start;
3669   static lives_colRGBA64_t vfade_in_col;
3670   static lives_colRGBA64_t vfade_out_col;
3671 #endif
3672 
3673   static lives_render_error_t read_write_error;
3674 
3675   static char nlabel[128];
3676 
3677   char *blabel = NULL;
3678   char *key_string, *com;
3679   char *filter_name;
3680 
3681   int i;
3682 
3683   if (reset) {
3684     LiVESList *list = NULL;
3685     r_audio = rend_audio;
3686     r_video = rend_video;
3687     progress = frame = 1;
3688     rec_delta_tc = 0;
3689     event = cfile->next_event;
3690     if (WEED_EVENT_IS_MARKER(event)) {
3691       if (weed_get_int_value(event, WEED_LEAF_LIVES_TYPE, &weed_error) == EVENT_MARKER_RECORD_START) {
3692         if (cfile->old_frames > 0) {
3693           /// tc delta is only used if we are rendering to an existing clip; otherwise resampling should have removed the event
3694           /// but just in case, we ignore it
3695           rec_delta_tc = weed_get_int64_value(event, WEED_LEAF_TCDELTA, NULL);
3696         }
3697       }
3698     }
3699 
3700     /// set audio and video start positions
3701     atc = q_gint64(get_event_timecode(event), cfile->fps);
3702     atime = (double)(atc + rec_delta_tc) / TICKS_PER_SECOND_DBL;
3703     out_frame = calc_frame_from_time4(mainw->current_file, atime);
3704 
3705     /// set the highest quality palette conversions
3706     init_conversions(LIVES_INTENTION_RENDER);
3707 
3708     if (cfile->frames < out_frame) out_frame = cfile->frames + 1;
3709     cfile->undo_start = out_frame;
3710 
3711     // store these, because if the user previews and there is no audio file yet, values may get reset
3712     cfile->undo_achans = cfile->achans;
3713     cfile->undo_arate = cfile->arate;
3714     cfile->undo_arps = cfile->arps;
3715     cfile->undo_asampsize = cfile->asampsize;
3716 
3717     // we may have set this to TRUE to stop the audio being clobbered; now we must reset it to get the correct audio filename
3718     cfile->opening = FALSE;
3719 
3720     clear_mainw_msg();
3721     mainw->filter_map = NULL;
3722     mainw->afilter_map = NULL;
3723     mainw->audio_event = event;
3724     old_scrap_frame = -1;
3725     rec_delta_tc = 0;
3726     /* end_tc */
3727     /*   = get_event_timecode(get_last_frame_event(mainw->event_list)) */
3728     /*   + TICKS_PER_SECOND_DBL / cfile->fps; */
3729 
3730 #ifdef VFADE_RENDER
3731     if (r_video) {
3732       if (mainw->vfade_in_secs > 0.) {
3733         vfade_in_end = q_gint64(mainw->vfade_in_secs * TICKS_PER_SECOND_DBL, cfile->fps);
3734         vfade_in_col = mainw->vfade_in_col;
3735       } else vfade_in_end = 0;
3736       if (mainw->vfade_out_secs > 0.) {
3737         vfade_out_start = q_gint64(end_tc - mainw->vfade_out_secs * TICKS_PER_SECOND_DBL, cfile->fps);
3738         vfade_out_col = mainw->vfade_out_col;
3739       } else vfade_out_start = end_tc;
3740     }
3741 #endif
3742 
3743     if (r_audio) {
3744       /// define the number of backing audio tracks (i.e tracks with audio but no video)
3745       natracks = nbtracks = 0;
3746       if (mainw->multitrack && mainw->multitrack->audio_vols) {
3747         list = mainw->multitrack->audio_vols;
3748         nbtracks = mainw->multitrack->opts.back_audio_tracks;
3749       }
3750 
3751       /// set (fixed) volume levels for input audio tracks
3752       for (i = 0; i < MAX_AUDIO_TRACKS; i++) {
3753         xaclips[i] = -1;
3754         xaseek[i] = xavel[i] = 0.;
3755         if (list) {
3756           natracks++;
3757           chvols[i] = (double)LIVES_POINTER_TO_INT(list->data) / 1000000.;
3758           list = list->next;
3759         } else chvols[i] = 0.;
3760       }
3761 
3762       if (!mainw->multitrack) {
3763         natracks = 1;
3764         chvols[0] = 1.;
3765       }
3766       /// alt label text for when we are rendering audio parts
3767       lives_snprintf(nlabel, 128, "%s", _("Rendering audio..."));
3768       read_write_error = LIVES_RENDER_ERROR_NONE;
3769       audio_free_fnames();
3770     }
3771     return LIVES_RENDER_READY;
3772   }
3773 
3774   if (mainw->effects_paused) return LIVES_RENDER_EFFECTS_PAUSED;
3775 
3776   if (mainw->flush_audio_tc != 0 || event) {
3777     if (event) etype = get_event_type(event);
3778     else etype = WEED_EVENT_TYPE_FRAME;
3779     if (mainw->flush_audio_tc == 0) {
3780       is_blank = FALSE;
3781       eventnext = get_next_event(event);
3782     } else {
3783       if (etype != WEED_EVENT_TYPE_MARKER)
3784         etype = WEED_EVENT_TYPE_FRAME;
3785     }
3786     if (!r_video && etype != WEED_EVENT_TYPE_FRAME) etype = WEED_EVENT_TYPE_UNDEFINED;
3787 
3788     switch (etype) {
3789     case WEED_EVENT_TYPE_MARKER: {
3790       int marker_type = weed_get_int_value(event, WEED_LEAF_LIVES_TYPE, &weed_error);
3791       if (marker_type == EVENT_MARKER_RECORD_START) {
3792         /// tc delta is only used if we are rendering to an existing clip; otherwise resampling should have removed the event
3793         /// but just in case, we ignore it
3794         if (cfile->old_frames > 0) {
3795           rec_delta_tc = weed_get_int64_value(event, WEED_LEAF_TCDELTA, NULL);
3796           atime = (double)(get_event_timecode(event) + rec_delta_tc) / TICKS_PER_SECOND_DBL;
3797           out_frame = calc_frame_from_time4(mainw->current_file, atime);
3798         }
3799       }
3800     }
3801     break;
3802     case WEED_EVENT_TYPE_FRAME:
3803       out_tc = (weed_timecode_t)((out_frame - 1) / cfile->fps
3804                                  * TICKS_PER_SECOND_DBL - rec_delta_tc); // calculate tc of next out frame */
3805       out_tc = q_gint64(out_tc, cfile->fps);
3806       next_out_tc = (weed_timecode_t)(out_frame / cfile->fps
3807                                       * TICKS_PER_SECOND_DBL - rec_delta_tc); // calculate tc of next out frame */
3808       next_out_tc = q_gint64(next_out_tc, cfile->fps);
3809       if (mainw->flush_audio_tc == 0) {
3810         tc = get_event_timecode(event);
3811 
3812         if (r_video && !(!mainw->clip_switched && cfile->hsize * cfile->vsize == 0)) {
3813           lives_freep((void **)&mainw->clip_index);
3814           lives_freep((void **)&mainw->frame_index);
3815 
3816           mainw->clip_index = weed_get_int_array_counted(event, WEED_LEAF_CLIPS, &mainw->num_tracks);
3817           mainw->frame_index = weed_get_int64_array(event, WEED_LEAF_FRAMES, &weed_error);
3818 
3819           if (mainw->scrap_file != -1) {
3820             for (i = 0; i < mainw->num_tracks; i++) {
3821               if (mainw->clip_index[i] != mainw->scrap_file) {
3822                 scrap_track = -1;
3823                 break;
3824               }
3825               if (scrap_track == -1) scrap_track = i;
3826             }
3827           }
3828           if (scrap_track != -1) {
3829             int64_t offs;
3830             // do not apply fx, just pull frame
3831             if (mainw->frame_index[scrap_track] == old_scrap_frame && mainw->scrap_pixbuf) {
3832               pixbuf = mainw->scrap_pixbuf;
3833             } else {
3834               if (mainw->scrap_pixbuf) {
3835 #ifndef SAVE_THREAD
3836                 lives_widget_object_unref(mainw->scrap_pixbuf);
3837 #endif
3838                 mainw->scrap_pixbuf = NULL;
3839               }
3840               old_scrap_frame = mainw->frame_index[scrap_track];
3841               layer = lives_layer_new_for_frame(mainw->clip_index[scrap_track], mainw->frame_index[scrap_track]);
3842               offs = weed_get_int64_value(event, WEED_LEAF_HOST_SCRAP_FILE_OFFSET, &weed_error);
3843               if (!mainw->files[mainw->scrap_file]->ext_src) load_from_scrap_file(NULL, -1);
3844               lives_lseek_buffered_rdonly_absolute(LIVES_POINTER_TO_INT(mainw->files[mainw->clip_index[scrap_track]]->ext_src),
3845                                                    offs);
3846               if (!pull_frame(layer, get_image_ext_for_type(cfile->img_type), tc)) {
3847                 weed_layer_free(layer);
3848                 layer = NULL;
3849               }
3850             }
3851           } else {
3852             int oclip, nclip;
3853             layers = (weed_plant_t **)lives_malloc((mainw->num_tracks + 1) * sizeof(weed_plant_t *));
3854             // get list of active tracks from mainw->filter map
3855             get_active_track_list(mainw->clip_index, mainw->num_tracks, mainw->filter_map);
3856             for (i = 0; i < mainw->num_tracks; i++) {
3857               oclip = mainw->old_active_track_list[i];
3858               mainw->ext_src_used[oclip] = FALSE;
3859               if (oclip > 0 && oclip == (nclip = mainw->active_track_list[i])) {
3860                 if (mainw->track_decoders[i] == mainw->files[oclip]->ext_src) mainw->ext_src_used[oclip] = TRUE;
3861               }
3862             }
3863 
3864             for (i = 0; i < mainw->num_tracks; i++) {
3865               if (mainw->clip_index[i] > 0 && mainw->frame_index[i] > 0 && mainw->multitrack) is_blank = FALSE;
3866               layers[i] = lives_layer_new_for_frame(mainw->clip_index[i], mainw->frame_index[i]);
3867               weed_layer_nullify_pixel_data(layers[i]);
3868 
3869               if ((oclip = mainw->old_active_track_list[i]) != (nclip = mainw->active_track_list[i])) {
3870                 // now using threading, we want to start pulling all pixel_data for all active layers here
3871                 // however, we may have more than one copy of the same clip - in this case we want to
3872                 // create clones of the decoder plugin
3873                 // this is to prevent constant seeking between different frames in the clip
3874 
3875                 // check if ext_src survives old->new
3876 
3877                 ////
3878                 if (oclip > 0) {
3879                   if (mainw->files[oclip]->clip_type == CLIP_TYPE_FILE) {
3880                     if (mainw->track_decoders[i] != (lives_decoder_t *)mainw->files[oclip]->ext_src) {
3881                       // remove the clone for oclip
3882                       close_decoder_plugin(mainw->track_decoders[i]);
3883                     } else chill_decoder_plugin(oclip); /// free bufferes to relesae memory
3884                     mainw->track_decoders[i] = NULL;
3885                   }
3886                 }
3887 
3888                 if (nclip > 0) {
3889                   if (mainw->files[nclip]->clip_type == CLIP_TYPE_FILE) {
3890                     if (!mainw->ext_src_used[nclip]) {
3891                       mainw->track_decoders[i] = (lives_decoder_t *)mainw->files[nclip]->ext_src;
3892                       mainw->ext_src_used[nclip] = TRUE;
3893                     } else {
3894                       // add new clone for nclip
3895                       mainw->track_decoders[i] = clone_decoder(nclip);
3896 		      // *INDENT-OFF*
3897                     }}}}
3898 	      // *INDENT-ON*
3899 
3900               mainw->old_active_track_list[i] = mainw->active_track_list[i];
3901 
3902               if (nclip > 0) {
3903                 const char *img_ext = get_image_ext_for_type(mainw->files[nclip]->img_type);
3904                 // set alt src in layer
3905                 weed_set_voidptr_value(layers[i], WEED_LEAF_HOST_DECODER, (void *)mainw->track_decoders[i]);
3906                 pull_frame_threaded(layers[i], img_ext, (weed_timecode_t)mainw->currticks, 0, 0);
3907               } else {
3908                 weed_layer_pixel_data_free(layers[i]);
3909               }
3910             }
3911             layers[i] = NULL;
3912 
3913             if ((inst = weed_get_plantptr_value(event, WEED_LEAF_HOST_EASING_END, NULL))) {
3914               easing = weed_get_int_value(event, WEED_LEAF_EASE_OUT, NULL);
3915               weed_set_int_value(inst, WEED_LEAF_EASE_OUT, easing);
3916               weed_set_boolean_value(inst, WEED_LEAF_AUTO_EASING, WEED_TRUE);
3917             }
3918 
3919             layer = weed_apply_effects(layers, mainw->filter_map, tc, cfile->hsize, cfile->vsize, pchains);
3920 
3921             for (i = 0; layers[i]; i++) {
3922               if (layer != layers[i]) {
3923                 check_layer_ready(layers[i]);
3924                 weed_layer_free(layers[i]);
3925               }
3926             }
3927             lives_free(layers);
3928           }
3929 #ifdef VFADE_RENDER
3930           if (layer) {
3931             double fadeamt;
3932             if (out_tc < vfade_in_end) {
3933               fadeamt = (double)(vfade_in_end - out_tc) / (double)vfade_in_end;
3934               weed_set_int_value(layer, "red_adjust", (double)vfade_in_col.red / 255.);
3935               weed_set_int_value(layer, "green_adjust", (double)vfade_in_col.green / 255.);
3936               weed_set_int_value(layer, "blue_adjust", (double)vfade_in_col.blue / 255.);
3937               weed_set_double_value(layer, "colorize", fadeamt);
3938             }
3939             if (out_tc > vfade_out_start) {
3940               fadeamt = (double)(out_tc - vfade_out_start) / (double)(end_tc - vfade_out_start);
3941               weed_set_int_value(layer, "red_adjust", (double)vfade_in_col.red / 255.);
3942               weed_set_int_value(layer, "green_adjust", (double)vfade_in_col.green / 255.);
3943               weed_set_int_value(layer, "blue_adjust", (double)vfade_in_col.blue / 255.);
3944               weed_set_double_value(layer, "colorize", fadeamt);
3945             }
3946           }
3947 #endif
3948           if (layer) {
3949             int lpal, width, height;
3950             boolean was_lbox = FALSE;
3951             if (mainw->transrend_proc) {
3952               if (lives_proc_thread_check(mainw->transrend_proc)) return LIVES_RENDER_ERROR;
3953               lives_nanosleep_until_nonzero(!mainw->transrend_ready);
3954               if (lives_proc_thread_check(mainw->transrend_proc)) return LIVES_RENDER_ERROR;
3955               mainw->transrend_layer = layer;
3956               mainw->transrend_ready = TRUE;
3957               break;
3958             }
3959             check_layer_ready(layer);
3960             width = weed_layer_get_width(layer) * weed_palette_get_pixels_per_macropixel(weed_layer_get_palette(layer));
3961             height = weed_layer_get_height(layer);
3962             lpal = layer_palette = weed_layer_get_palette(layer);
3963 #ifndef ALLOW_PNG24
3964             if (cfile->img_type == IMG_TYPE_JPEG && layer_palette != WEED_PALETTE_RGB24
3965                 && layer_palette != WEED_PALETTE_RGBA32)
3966               layer_palette = WEED_PALETTE_RGB24;
3967 
3968             else if (cfile->img_type == IMG_TYPE_PNG && layer_palette != WEED_PALETTE_RGBA32)
3969               layer_palette = WEED_PALETTE_RGBA32;
3970 #else
3971             layer_palette = WEED_PALETTE_RGB24;
3972 #endif
3973             if ((mainw->multitrack && prefs->letterbox_mt) || (prefs->letterbox && !mainw->multitrack)) {
3974               calc_maxspect(cfile->hsize, cfile->vsize, &width, &height);
3975               if (layer_palette != lpal && (cfile->hsize > width || cfile->vsize > height)) {
3976                 convert_layer_palette(layer, layer_palette, 0);
3977               }
3978               letterbox_layer(layer, cfile->hsize, cfile->vsize, width, height, LIVES_INTERP_BEST, layer_palette, 0);
3979               was_lbox = TRUE;
3980             } else {
3981               resize_layer(layer, cfile->hsize, cfile->vsize, LIVES_INTERP_BEST, layer_palette, 0);
3982             }
3983 
3984             convert_layer_palette(layer, layer_palette, 0);
3985 
3986             // we have a choice here, we can either render with the same gamma tf as cfile, or force it to sRGB
3987             if (!was_lbox)
3988               gamma_convert_layer(cfile->gamma_type, layer);
3989             else
3990               gamma_convert_sub_layer(cfile->gamma_type, 1.0, layer, (cfile->hsize - width) / 2,
3991                                       (cfile->vsize - height) / 2,
3992                                       width, height, TRUE);
3993 
3994             if (weed_plant_has_leaf(event, WEED_LEAF_OVERLAY_TEXT)) {
3995               char *texto = weed_get_string_value(event, WEED_LEAF_OVERLAY_TEXT, NULL);
3996               render_text_overlay(layer, texto);
3997               lives_free(texto);
3998             }
3999             pixbuf = layer_to_pixbuf(layer, TRUE, FALSE);
4000             weed_layer_free(layer);
4001           }
4002           mainw->blend_file = blend_file;
4003         }
4004         next_frame_event = get_next_frame_event(event);
4005       } else tc = mainw->flush_audio_tc;
4006 
4007       if (r_audio && (!next_frame_event || WEED_EVENT_IS_AUDIO_FRAME(event)
4008                       || (mainw->flush_audio_tc != 0 && tc > mainw->flush_audio_tc)) && tc > atc) {
4009         int auditracks;
4010         for (auditracks = 0; auditracks < MAX_AUDIO_TRACKS; auditracks++) {
4011           // see if we have any audio to render
4012           if (xavel[auditracks] != 0.) break;
4013         }
4014 
4015         cfile->achans = cfile->undo_achans;
4016         cfile->arate = cfile->undo_arate;
4017         cfile->arps = cfile->undo_arps;
4018         cfile->asampsize = cfile->undo_asampsize;
4019 
4020         blabel = set_proc_label(mainw->proc_ptr, nlabel, TRUE);
4021 
4022         lives_freep((void **)&THREADVAR(read_failed_file));
4023 
4024         if (mainw->flush_audio_tc != 0) dtc = mainw->flush_audio_tc;
4025         else dtc = q_gint64(tc + rec_delta_tc, cfile->fps);
4026 
4027         if (auditracks < MAX_AUDIO_TRACKS) {
4028           // render audio
4029           render_audio_segment(natracks, xaclips, mainw->multitrack != NULL ? mainw->multitrack->render_file :
4030                                mainw->current_file, xavel, xaseek, atc, dtc, chvols, 1., 1., NULL);
4031         } else {
4032           // render silence
4033           render_audio_segment(1, NULL, mainw->multitrack != NULL ? mainw->multitrack->render_file : mainw->current_file,
4034                                NULL, NULL, atc, dtc, chvols, 0., 0., NULL);
4035         }
4036 
4037         atc = dtc;
4038 
4039         if (THREADVAR(write_failed)) {
4040           int outfile = (mainw->multitrack ? mainw->multitrack->render_file : mainw->current_file);
4041           char *outfilename = lives_get_audio_file_name(outfile);
4042           do_write_failed_error_s(outfilename, NULL);
4043           lives_free(outfilename);
4044           read_write_error = LIVES_RENDER_ERROR_WRITE_AUDIO;
4045         }
4046 
4047         if (THREADVAR(read_failed)) {
4048           do_read_failed_error_s(THREADVAR(read_failed_file), NULL);
4049           read_write_error = LIVES_RENDER_ERROR_READ_AUDIO;
4050         }
4051 
4052         set_proc_label(mainw->proc_ptr, blabel, FALSE);
4053         lives_freep((void **)&blabel);
4054       }
4055 
4056       if (mainw->flush_audio_tc != 0) {
4057         if (read_write_error) return read_write_error;
4058         return LIVES_RENDER_COMPLETE;
4059       } else {
4060         int *aclips = NULL;
4061         double *aseeks = NULL;
4062         int num_aclips = weed_frame_event_get_audio_tracks(event, &aclips, &aseeks);
4063 
4064         for (i = 0; i < num_aclips; i += 2) {
4065           if (aclips[i + 1] > 0) { // clipnum
4066             double mult = 1.0;
4067             mytrack = aclips[i] + nbtracks;
4068             if (mytrack < 0) mytrack = 0;
4069             //g_print("del was %f\n", xaseek[mytrack] - aseeks[i]);
4070             if (prefs->rr_super && prefs->rr_ramicro) {
4071               /// smooth out audio by ignoring tiny seek differences
4072               if (xavel[mytrack] * aseeks[i + 1] < 0.) mult *= AUD_DIFF_REVADJ;
4073               if (xaclips[mytrack] != aclips[i + 1] || fabs(xaseek[mytrack] - aseeks[i]) > AUD_DIFF_MIN * mult)
4074                 xaseek[mytrack] = aseeks[i];
4075             }
4076             xaclips[mytrack] = aclips[i + 1];
4077             xavel[mytrack] = aseeks[i + 1];
4078           }
4079         }
4080         lives_freep((void **)&aseeks);
4081         lives_freep((void **)&aclips);
4082       }
4083 
4084       if (!r_video) break;
4085       if (!pixbuf) break;
4086       if (!next_frame_event && is_blank) break; // don't render final blank frame
4087 
4088       if (next_frame_event) {
4089         weed_timecode_t next_tc = get_event_timecode(next_frame_event);
4090         if (next_tc < next_out_tc || next_tc - next_out_tc < next_out_tc - tc) break;
4091       } else if (next_out_tc > tc) break;
4092 
4093 #ifndef SAVE_THREAD
4094       if (cfile->old_frames > 0) {
4095         tmp = make_image_file_name(cfile, out_frame, LIVES_FILE_EXT_MGK);
4096       } else {
4097         tmp = make_image_file_name(cfile, out_frame, get_image_ext_for_type(cfile->img_type));
4098       }
4099       lives_snprintf(oname, PATH_MAX, "%s", tmp);
4100       lives_free(tmp);
4101 
4102       do {
4103         retval = LIVES_RESPONSE_NONE;
4104         lives_pixbuf_save(pixbuf, oname, cfile->img_type, 100 - prefs->ocp, cfile->hsize, cfile->vsize, NULL);
4105 
4106         if (error) {
4107           retval = do_write_failed_error_s_with_retry(oname, error->message, NULL);
4108           lives_error_free(error);
4109           error = NULL;
4110           if (retval != LIVES_RESPONSE_RETRY) read_write_error = LIVES_RENDER_ERROR_WRITE_FRAME;
4111         }
4112       } while (retval == LIVES_RESPONSE_RETRY);
4113 
4114 #else
4115       if (!saver_thread) {
4116         if (!mainw->transrend_proc) {
4117           saveargs = (savethread_priv_t *)lives_calloc(1, sizeof(savethread_priv_t));
4118           saveargs->img_type = cfile->img_type;
4119           saveargs->compression = 100 - prefs->ocp;
4120           saveargs->width = cfile->hsize;
4121           saveargs->height = cfile->vsize;
4122           saver_thread = (lives_thread_t *)lives_calloc(1, sizeof(lives_thread_t));
4123         }
4124       } else {
4125         lives_thread_join(*saver_thread, NULL);
4126         while (saveargs->error) {
4127           retval = do_write_failed_error_s_with_retry(saveargs->fname, saveargs->error->message);
4128           lives_error_free(saveargs->error);
4129           saveargs->error = NULL;
4130           if (retval != LIVES_RESPONSE_RETRY) {
4131             read_write_error = LIVES_RENDER_ERROR_WRITE_FRAME;
4132             break;
4133           }
4134           lives_pixbuf_save(saveargs->pixbuf, saveargs->fname, saveargs->img_type, saveargs->compression,
4135                             saveargs->width, saveargs->height, &saveargs->error);
4136         }
4137 
4138         if (saveargs->pixbuf && saveargs->pixbuf != pixbuf) {
4139           if (saveargs->pixbuf == mainw->scrap_pixbuf) mainw->scrap_pixbuf = NULL;
4140           lives_widget_object_unref(saveargs->pixbuf);
4141           saveargs->pixbuf = NULL;
4142         }
4143         lives_free(saveargs->fname);
4144         saveargs->fname = NULL;
4145       }
4146 
4147       if (!mainw->transrend_proc) {
4148         if (cfile->old_frames > 0) {
4149           saveargs->fname = make_image_file_name(cfile, out_frame, LIVES_FILE_EXT_MGK);
4150         } else {
4151           saveargs->fname = make_image_file_name(cfile, out_frame, get_image_ext_for_type(cfile->img_type));
4152         }
4153 
4154         saveargs->pixbuf = pixbuf;
4155         lives_thread_create(saver_thread, LIVES_THRDATTR_NONE, lives_pixbuf_save_threaded, saveargs);
4156       }
4157 #endif
4158 
4159       // sig_progress...
4160       lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%d", progress++);
4161 
4162       if (cfile->undo_start == -1) cfile->undo_start = out_frame;
4163       cfile->undo_end = out_frame;
4164       if (out_frame > cfile->frames) cfile->frames = out_frame;
4165       if (out_frame > cfile->end) cfile->end = out_frame;
4166       if (cfile->start == 0) cfile->start = 1;
4167       out_frame++;
4168 
4169       // if our pixbuf came from scrap file, and next frame is also from scrap file with same frame number,
4170       // save the pixbuf and re-use it
4171       if (scrap_track != -1) mainw->scrap_pixbuf = pixbuf;
4172       break;
4173 
4174     case WEED_EVENT_TYPE_FILTER_INIT:
4175       // effect init
4176       //  bind the weed_fx to next free key/0
4177 
4178       filter_name = weed_get_string_value(event, WEED_LEAF_FILTER, &weed_error);
4179       // for now, assume we can find hashname
4180       idx = weed_get_idx_for_hashname(filter_name, TRUE);
4181       lives_free(filter_name);
4182 
4183       filter = get_weed_filter(idx);
4184       if (is_pure_audio(filter, FALSE)) {
4185         if (weed_plant_has_leaf(event, WEED_LEAF_HOST_TAG)) weed_leaf_delete(event, WEED_LEAF_HOST_TAG);
4186         break; // audio effects are processed in the audio renderer
4187       }
4188 
4189       key = get_next_free_key();
4190       weed_add_effectkey_by_idx(key + 1, idx);
4191       key_string = lives_strdup_printf("%d", key);
4192       weed_set_string_value(event, WEED_LEAF_HOST_TAG, key_string);
4193       lives_free(key_string);
4194 
4195       if (weed_plant_has_leaf(event, WEED_LEAF_IN_COUNT)) {
4196         in_count = weed_get_int_array_counted(event, WEED_LEAF_IN_COUNT, &num_in_count);
4197       }
4198 
4199       citmpl = weed_get_plantptr_array_counted(filter, WEED_LEAF_IN_CHANNEL_TEMPLATES, &num_in_channels);
4200       if (num_in_channels != num_in_count) {
4201         LIVES_ERROR("num_in_count != num_in_channels");
4202       } else {
4203         if (num_in_channels > 0) {
4204           bitmpl = (weed_plant_t **)lives_malloc(num_in_channels * sizeof(weed_plant_t *));
4205           for (i = 0; i < num_in_channels; i++) {
4206             bitmpl[i] = weed_plant_copy(citmpl[i]);
4207             if (in_count[i] > 0) {
4208               weed_set_boolean_value(citmpl[i], WEED_LEAF_HOST_DISABLED, WEED_FALSE);
4209               weed_set_int_value(citmpl[i], WEED_LEAF_HOST_REPEATS, in_count[i]);
4210             } else weed_set_boolean_value(citmpl[i], WEED_LEAF_HOST_DISABLED, WEED_TRUE);
4211           }
4212         }
4213       }
4214 
4215       lives_freep((void **)&in_count);
4216 
4217       cotmpl = weed_get_plantptr_array_counted(filter, WEED_LEAF_OUT_CHANNEL_TEMPLATES, &num_out_channels);
4218       if (num_out_channels > 0) {
4219         botmpl = (weed_plant_t **)lives_malloc(num_out_channels * sizeof(weed_plant_t *));
4220         for (i = 0; i < num_out_channels; i++) {
4221           botmpl[i] = weed_plant_copy(cotmpl[i]);
4222           if (!weed_plant_has_leaf(cotmpl[i], WEED_LEAF_HOST_DISABLED))
4223             weed_set_boolean_value(cotmpl[i], WEED_LEAF_HOST_DISABLED, WEED_FALSE);
4224         }
4225       }
4226 
4227       weed_init_effect(key);
4228 
4229       // restore channel state / number from backup
4230 
4231       if (num_in_channels > 0) {
4232         for (i = 0; i < num_in_channels; i++) {
4233           weed_leaf_copy_or_delete(citmpl[i], WEED_LEAF_HOST_DISABLED, bitmpl[i]);
4234           weed_leaf_copy_or_delete(citmpl[i], WEED_LEAF_HOST_REPEATS, bitmpl[i]);
4235           weed_plant_free(bitmpl[i]);
4236         }
4237         lives_free(bitmpl);
4238         lives_free(citmpl);
4239       }
4240 
4241       if (num_out_channels > 0) {
4242         for (i = 0; i < num_out_channels; i++) {
4243           weed_leaf_copy_or_delete(cotmpl[i], WEED_LEAF_HOST_DISABLED, botmpl[i]);
4244           weed_leaf_copy_or_delete(cotmpl[i], WEED_LEAF_HOST_REPEATS, botmpl[i]);
4245           weed_plant_free(botmpl[i]);
4246         }
4247         lives_free(botmpl);
4248         lives_free(cotmpl);
4249       }
4250 
4251       // reinit effect with saved parameters
4252       orig_inst = inst = rte_keymode_get_instance(key + 1, 0);
4253 
4254       if (weed_plant_has_leaf(event, WEED_LEAF_IN_PARAMETERS)) {
4255         int nparams;
4256         void **xpchains = weed_get_voidptr_array_counted(event, WEED_LEAF_IN_PARAMETERS, &nparams);
4257         pchains[key] = (void **)lives_realloc(pchains[key], (nparams + 1) * sizeof(void *));
4258         for (i = 0; i < nparams; i++) pchains[key][i] = xpchains[i];
4259         pchains[key][nparams] = NULL;
4260         lives_free(xpchains);
4261       } else pchains[key] = NULL;
4262 
4263 filterinit2:
4264 
4265       num_params = num_in_params(inst, FALSE, FALSE);
4266 
4267       if (num_params > 0) {
4268         weed_call_deinit_func(inst);
4269         if (weed_plant_has_leaf(event, WEED_LEAF_IN_PARAMETERS)) {
4270           source_params = (weed_plant_t **)pchains[key];
4271           in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, &weed_error);
4272 
4273           for (i = 0; i < num_params; i++) {
4274             if (source_params && source_params[i + offset] && is_init_pchange(event, source_params[i + offset]))
4275               weed_leaf_copy(in_params[i], WEED_LEAF_VALUE, source_params[i + offset], WEED_LEAF_VALUE);
4276           }
4277           lives_free(in_params);
4278         }
4279 
4280         offset += num_params;
4281 
4282         filter = weed_instance_get_filter(inst, FALSE);
4283 
4284         if (weed_plant_has_leaf(filter, WEED_LEAF_INIT_FUNC)) {
4285           weed_init_f init_func = (weed_init_f)weed_get_funcptr_value(filter, WEED_LEAF_INIT_FUNC, NULL);
4286           if (init_func) {
4287             char *cwd = cd_to_plugin_dir(filter);
4288             (*init_func)(inst);
4289             lives_chdir(cwd, FALSE);
4290             lives_free(cwd);
4291           }
4292         }
4293 
4294         weed_set_boolean_value(inst, WEED_LEAF_HOST_INITED, WEED_TRUE);
4295         weed_set_boolean_value(inst, WEED_LEAF_HOST_UNUSED, WEED_TRUE);
4296       }
4297 
4298       if (weed_plant_has_leaf(event, WEED_LEAF_HOST_KEY)) {
4299         // mt events will not have this;
4300         // it is used to connect params and alpha channels during rendering
4301         // holds our original key/mode values
4302 
4303         int hostkey = weed_get_int_value(event, WEED_LEAF_HOST_KEY, &weed_error);
4304         int hostmode = weed_get_int_value(event, WEED_LEAF_HOST_MODE, &weed_error);
4305 
4306         weed_set_int_value(inst, WEED_LEAF_HOST_KEY, hostkey);
4307         weed_set_int_value(inst, WEED_LEAF_HOST_MODE, hostmode);
4308 
4309         if ((easing = weed_get_int_value(event, WEED_LEAF_EASE_OUT, NULL)) > 0) {
4310           weed_plant_t *deinit = weed_get_plantptr_value(event, WEED_LEAF_DEINIT_EVENT, NULL);
4311           if (deinit) {
4312             weed_plant_t *xevent = deinit;
4313             for (i = 0; i < easing; i++) {
4314               xevent = get_prev_frame_event(xevent);
4315             }
4316             if (xevent != deinit && xevent) {
4317               weed_set_int_value(xevent, WEED_LEAF_EASE_OUT, easing);
4318               weed_set_plantptr_value(xevent, WEED_LEAF_HOST_EASING_END, inst);
4319 	      // *INDENT-OFF*
4320             }}}}
4321 	  // *INDENT-ON*
4322 
4323       if (weed_plant_has_leaf(inst, WEED_LEAF_HOST_NEXT_INSTANCE)) {
4324         // handle compound fx
4325         inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, &weed_error);
4326         goto filterinit2;
4327       }
4328       weed_instance_unref(orig_inst);
4329 
4330       break;
4331     case WEED_EVENT_TYPE_FILTER_DEINIT:
4332       init_event = weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, &weed_error);
4333 
4334       filter_name = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_FILTER, &weed_error);
4335       // for now, assume we can find hashname
4336       idx = weed_get_idx_for_hashname(filter_name, TRUE);
4337       lives_free(filter_name);
4338 
4339       filter = get_weed_filter(idx);
4340       if (is_pure_audio(filter, FALSE)) break; // audio effects are processed in the audio renderer
4341 
4342       key_string = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_HOST_TAG, &weed_error);
4343       key = atoi(key_string);
4344       lives_free(key_string);
4345       if ((inst = rte_keymode_get_instance(key + 1, 0))) {
4346         weed_delete_effectkey(key + 1, 0);
4347         weed_instance_unref(inst);
4348       }
4349       // no freep !
4350       if (pchains[key]) lives_free(pchains[key]);
4351       pchains[key] = NULL;
4352       break;
4353     case WEED_EVENT_TYPE_PARAM_CHANGE:
4354       if (!mainw->multitrack) {
4355         init_event = weed_get_voidptr_value((weed_plant_t *)event, WEED_LEAF_INIT_EVENT, NULL);
4356         if (weed_plant_has_leaf((weed_plant_t *)init_event, WEED_LEAF_HOST_TAG)) {
4357           key_string = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_HOST_TAG, NULL);
4358           key = atoi(key_string);
4359           lives_free(key_string);
4360 
4361           filter_name = weed_get_string_value((weed_plant_t *)init_event, WEED_LEAF_FILTER, NULL);
4362           idx = weed_get_idx_for_hashname(filter_name, TRUE);
4363           lives_free(filter_name);
4364 
4365           filter = get_weed_filter(idx);
4366 
4367           if (is_pure_audio(filter, FALSE)) break; // audio effects are processed in the audio renderer
4368 
4369           if ((inst = rte_keymode_get_instance(key + 1, 0))) {
4370             int pnum = weed_get_int_value(event, WEED_LEAF_INDEX, NULL);
4371             weed_plant_t *param = weed_inst_in_param(inst, pnum, FALSE, FALSE);
4372             weed_leaf_dup(param, event, WEED_LEAF_VALUE);
4373           }
4374         }
4375       }
4376       break;
4377     case WEED_EVENT_TYPE_FILTER_MAP:
4378 #ifdef DEBUG_EVENTS
4379       g_print("got new effect map\n");
4380 #endif
4381       mainw->filter_map = event;
4382       break;
4383     default: break;
4384     }
4385     event = eventnext;
4386   } else {
4387     /// no more events or audio to flush, rendering complete
4388 #ifdef SAVE_THREAD
4389     if (saver_thread) {
4390       lives_thread_join(*saver_thread, NULL);
4391       while (saveargs->error) {
4392         retval = do_write_failed_error_s_with_retry(saveargs->fname, saveargs->error->message);
4393         lives_error_free(saveargs->error);
4394         saveargs->error = NULL;
4395         if (retval != LIVES_RESPONSE_RETRY) read_write_error = LIVES_RENDER_ERROR_WRITE_FRAME;
4396         else lives_pixbuf_save(saveargs->pixbuf, saveargs->fname, saveargs->img_type, saveargs->compression,
4397                                  saveargs->width, saveargs->height, &saveargs->error);
4398       }
4399       if (saveargs->pixbuf) {
4400         lives_widget_object_unref(saveargs->pixbuf);
4401         if (saveargs->pixbuf == mainw->scrap_pixbuf) mainw->scrap_pixbuf = NULL;
4402       }
4403       lives_freep((void **)&saveargs->fname);
4404       lives_free(saveargs);
4405       lives_free(saver_thread);
4406       saver_thread = NULL;
4407       saveargs = NULL;
4408     }
4409 #endif
4410 
4411     if (cfile->old_frames == 0) cfile->undo_start = cfile->undo_end = 0;
4412     if (r_video) {
4413 
4414       com = lives_strdup_printf("%s mv_mgk \"%s\" %d %d \"%s\"", prefs->backend, cfile->handle, cfile->undo_start,
4415                                 cfile->undo_end, get_image_ext_for_type(cfile->img_type));
4416 
4417       lives_rm(cfile->info_file);
4418       mainw->error = FALSE;
4419       mainw->cancelled = CANCEL_NONE;
4420 
4421       lives_system(com, FALSE);
4422       lives_free(com);
4423       mainw->is_rendering = mainw->internal_messaging = FALSE;
4424 
4425       if (THREADVAR(com_failed)) {
4426         read_write_error = LIVES_RENDER_ERROR_WRITE_FRAME;
4427         // cfile->may_be_damaged = TRUE;
4428       }
4429     } else lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "completed");
4430 
4431     if (r_audio) {
4432       render_audio_segment(1, NULL, mainw->multitrack != NULL
4433                            ? mainw->multitrack->render_file : mainw->current_file,
4434                            NULL, NULL, atc, next_out_tc, chvols, 0., 0., NULL);
4435       cfile->afilesize = reget_afilesize_inner(mainw->current_file);
4436     }
4437     mainw->filter_map = NULL;
4438     mainw->afilter_map = NULL;
4439     completed = TRUE;
4440   }
4441 
4442   if (read_write_error) return read_write_error;
4443   if (completed) return LIVES_RENDER_COMPLETE;
4444   return LIVES_RENDER_PROCESSING;
4445 }
4446 
render_events_cb(boolean dummy)4447 lives_render_error_t render_events_cb(boolean dummy) {
4448   /// values ignored if first param is FALSE
4449   return render_events(FALSE, FALSE, FALSE);
4450 }
4451 
4452 
start_render_effect_events(weed_plant_t * event_list,boolean render_vid,boolean render_aud)4453 boolean start_render_effect_events(weed_plant_t *event_list, boolean render_vid, boolean render_aud) {
4454   // this is called to begin rendering effect events from an event_list into cfile
4455   // it will do a reorder/resample/resize/effect apply all in one pass
4456 
4457   // return FALSE in case of serious error
4458 
4459   double old_pb_fps = cfile->pb_fps;
4460 
4461   int oundo_start = cfile->undo_start;
4462   int oundo_end = cfile->undo_end;
4463 
4464   char *com;
4465 
4466   if (!event_list || (!render_vid && !render_aud)) return TRUE; //oh, that was easy !
4467 
4468   mainw->is_rendering = mainw->internal_messaging = TRUE;
4469   cfile->next_event = get_first_event(event_list);
4470 
4471   mainw->effort = -EFFORT_RANGE_MAX;
4472 
4473   mainw->progress_fn = &render_events_cb;
4474   render_events(TRUE, render_vid, render_aud);
4475 
4476   cfile->progress_start = 1;
4477   cfile->progress_end = count_resampled_events(event_list, cfile->fps);
4478 
4479   cfile->pb_fps = 1000000.;
4480 
4481   cfile->redoable = cfile->undoable = FALSE;
4482   lives_widget_set_sensitive(mainw->redo, FALSE);
4483   lives_widget_set_sensitive(mainw->undo, FALSE);
4484 
4485   cfile->undo_action = UNDO_RENDER;
4486 
4487   // clear up any leftover old files
4488   com = lives_strdup_printf("%s clear_tmp_files \"%s\"", prefs->backend, cfile->handle);
4489   lives_system(com, FALSE);
4490   lives_free(com);
4491 
4492   if (!mainw->transrend_proc) mainw->disk_mon = MONITOR_QUOTA;
4493 
4494   // play back the file as fast as possible, each time calling render_events()
4495   if ((!do_progress_dialog(TRUE, TRUE, render_vid ? (!mainw->transrend_proc ? _("Rendering")
4496                            : _("Transcoding")) : _("Pre-rendering audio"))
4497        && mainw->cancelled != CANCEL_KEEP) || mainw->error ||
4498       mainw->render_error >= LIVES_RENDER_ERROR
4499      ) {
4500     mainw->disk_mon = 0;
4501     mainw->cancel_type = CANCEL_KILL;
4502     mainw->cancelled = CANCEL_NONE;
4503 
4504     if (mainw->error) {
4505       widget_opts.non_modal = TRUE;
4506       do_error_dialog(mainw->msg);
4507       widget_opts.non_modal = FALSE;
4508       d_print_failed();
4509     } else if (mainw->render_error >= LIVES_RENDER_ERROR) d_print_failed();
4510     cfile->undo_start = oundo_start;
4511     cfile->undo_end = oundo_end;
4512     cfile->pb_fps = old_pb_fps;
4513     cfile->frames = cfile->old_frames;
4514     mainw->internal_messaging = FALSE;
4515     mainw->resizing = FALSE;
4516     cfile->next_event = NULL;
4517     return FALSE;
4518   }
4519 
4520   mainw->disk_mon = 0;
4521   mainw->cancel_type = CANCEL_KILL;
4522   mainw->cancelled = CANCEL_NONE;
4523   cfile->changed = TRUE;
4524   reget_afilesize(mainw->current_file);
4525   get_total_time(cfile);
4526 
4527   if (CLIP_TOTAL_TIME(mainw->current_file) == 0.) {
4528     d_print(_("nothing rendered.\n"));
4529     return FALSE;
4530   }
4531 
4532   lives_widget_set_sensitive(mainw->undo, TRUE);
4533   cfile->undoable = TRUE;
4534   cfile->pb_fps = old_pb_fps;
4535   lives_widget_set_sensitive(mainw->select_last, TRUE);
4536   set_undoable(_("rendering"), TRUE);
4537   cfile->next_event = NULL;
4538   return TRUE;
4539 }
4540 
4541 
count_events(weed_plant_t * event_list,boolean all_events,weed_timecode_t start_tc,weed_timecode_t end_tc)4542 int count_events(weed_plant_t *event_list, boolean all_events, weed_timecode_t start_tc, weed_timecode_t end_tc) {
4543   weed_plant_t *event;
4544   weed_timecode_t tc;
4545   int i = 0;
4546 
4547   if (!event_list) return 0;
4548   event = get_first_event(event_list);
4549 
4550   while (event) {
4551     tc = get_event_timecode(event);
4552     if ((all_events || (WEED_EVENT_IS_FRAME(event) && !WEED_EVENT_IS_AUDIO_FRAME(event))) &&
4553         (end_tc == 0 || (tc >= start_tc && tc < end_tc))) i++;
4554     event = get_next_event(event);
4555   }
4556   return i;
4557 }
4558 
4559 
count_resampled_events(weed_plant_t * event_list,double fps)4560 frames_t count_resampled_events(weed_plant_t *event_list, double fps) {
4561   weed_plant_t *event;
4562   weed_timecode_t tc, seg_start_tc = 0, seg_end_tc = 0;
4563 
4564   frames_t rframes = 0;
4565   int etype, marker_type;
4566 
4567   boolean seg_start = FALSE;
4568 
4569   if (!event_list) return 0;
4570   event = get_first_event(event_list);
4571 
4572   while (event) {
4573     etype = get_event_type(event);
4574     if (etype == WEED_EVENT_TYPE_FRAME) {
4575       tc = get_event_timecode(event);
4576       if (!seg_start) {
4577         seg_start_tc = seg_end_tc = tc;
4578         seg_start = TRUE;
4579       } else {
4580         seg_end_tc = tc;
4581       }
4582     } else {
4583       if (etype == WEED_EVENT_TYPE_MARKER) {
4584         marker_type = weed_get_int_value(event, WEED_LEAF_LIVES_TYPE, NULL);
4585         if (marker_type == EVENT_MARKER_RECORD_END) {
4586           // add (resampled) frames for one recording stretch
4587           if (seg_start) rframes += 1 + ((double)(seg_end_tc - seg_start_tc)) / TICKS_PER_SECOND_DBL * fps;
4588           seg_start = FALSE;
4589         }
4590       }
4591     }
4592     event = get_next_event(event);
4593   }
4594 
4595   if (seg_start) rframes += 1 + ((double)(seg_end_tc - seg_start_tc)) / TICKS_PER_SECOND_DBL * fps;
4596 
4597   return rframes;
4598 }
4599 
4600 
event_list_get_end_tc(weed_plant_t * event_list)4601 weed_timecode_t event_list_get_end_tc(weed_plant_t *event_list) {
4602   if (!event_list  || !get_last_event(event_list)) return 0.;
4603   return get_event_timecode(get_last_event(event_list));
4604 }
4605 
4606 
event_list_get_end_secs(weed_plant_t * event_list)4607 double event_list_get_end_secs(weed_plant_t *event_list) {
4608   return (event_list_get_end_tc(event_list) / TICKS_PER_SECOND_DBL);
4609 }
4610 
4611 
event_list_get_start_tc(weed_plant_t * event_list)4612 weed_timecode_t event_list_get_start_tc(weed_plant_t *event_list) {
4613   if (!event_list || !get_first_event(event_list)) return 0.;
4614   return get_event_timecode(get_first_event(event_list));
4615 }
4616 
4617 
event_list_get_start_secs(weed_plant_t * event_list)4618 double event_list_get_start_secs(weed_plant_t *event_list) {
4619   return (event_list_get_start_tc(event_list) / TICKS_PER_SECOND_DBL);
4620 }
4621 
4622 
has_audio_frame(weed_plant_t * event_list)4623 boolean has_audio_frame(weed_plant_t *event_list) {
4624   weed_plant_t *event = get_first_frame_event(event_list);
4625   while (event) {
4626     if (WEED_EVENT_IS_AUDIO_FRAME(event)) return TRUE;
4627     event = get_next_frame_event(event);
4628   }
4629   return FALSE;
4630 }
4631 
4632 
4633 ///////////////////////////////////////////////////////////////////
4634 
render_to_clip(boolean new_clip,boolean transcode)4635 boolean render_to_clip(boolean new_clip, boolean transcode) {
4636   // this function is called to actually start rendering mainw->event_list to a new/current clip
4637   char *pname = NULL;
4638   char *com, *tmp, *clipname = NULL;
4639   double old_fps = 0.;
4640   double afade_in_secs = 0., afade_out_secs = 0.;
4641 #ifdef VFADE_RENDER
4642   double vfade_in_secs = 0., vfade_out_secs = 0.;
4643   LiVESWidgetColor fadecol;
4644   lives_colRGBA64_t vfade_rgb;
4645 #endif
4646   boolean retval = TRUE, rendaud = TRUE, response;
4647   boolean norm_after = FALSE;
4648   int xachans = 0, xarate = 0, xasamps = 0, xse = 0;
4649   int current_file = mainw->current_file;
4650 
4651   if (new_clip) {
4652     if (prefs->render_prompt) {
4653       //set file details
4654       rdet = create_render_details(transcode ? 5 : 2);
4655 
4656       if (!has_audio_frame(mainw->event_list)) {
4657         lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton), FALSE);
4658         lives_widget_set_sensitive(resaudw->aud_checkbutton, FALSE);
4659       }
4660       rdet->enc_changed = FALSE;
4661       do {
4662         rdet->suggestion_followed = FALSE;
4663         response = lives_dialog_run(LIVES_DIALOG(rdet->dialog));
4664         if (response == LIVES_RESPONSE_OK && rdet->enc_changed) {
4665           check_encoder_restrictions(FALSE, TRUE, TRUE);
4666         }
4667       } while (rdet->suggestion_followed || response == LIVES_RESPONSE_RETRY || response == LIVES_RESPONSE_RESET);
4668 
4669       xarate = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_arate)));
4670       xachans = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_achans)));
4671       xasamps = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)));
4672 
4673       // do we render audio ?
4674       rendaud = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton));
4675       norm_after = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(rdet->norm_after));
4676       if (lives_widget_is_sensitive(rdet->afade_in))
4677         afade_in_secs = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rdet->afade_in));
4678       if (lives_widget_is_sensitive(rdet->afade_out))
4679         afade_out_secs = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rdet->afade_out));
4680 
4681       /* if (lives_widget_is_sensitive(rdet->vfade_in)) */
4682       /* 	vfade_in_secs = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rdet->vfade_in)); */
4683       /* if (lives_widget_is_sensitive(rdet->vfade_out)) */
4684       /* 	vfade_out_secs = lives_spin_button_get_value(LIVES_SPIN_BUTTON(rdet->vfade_out)); */
4685 
4686       /* lives_color_button_get_color(LIVES_COLOR_BUTTON(rdet->vfade_col), &fadecol); */
4687       /* widget_color_to_lives_rgba(&vfade_rgb, &fadecol); */
4688 
4689       if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned))) {
4690         xse = AFORM_UNSIGNED;
4691       } else xse = AFORM_SIGNED;
4692       if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend))) {
4693         xse |= AFORM_BIG_ENDIAN;
4694       } else xse |= AFORM_LITTLE_ENDIAN;
4695 
4696       if (!transcode) {
4697         clipname = lives_strdup(lives_entry_get_text(LIVES_ENTRY(rdet->clipname_entry)));
4698         tmp = get_untitled_name(mainw->untitled_number);
4699         if (!strcmp(clipname, tmp)) mainw->untitled_number++;
4700         lives_free(tmp);
4701       } else clipname = lives_strdup("transcode");
4702 
4703       lives_widget_destroy(rdet->dialog);
4704 
4705       if (response == LIVES_RESPONSE_CANCEL) {
4706         if (!transcode) lives_free(rdet->encoder_name);
4707         lives_free(clipname);
4708         lives_freep((void **)&rdet);
4709         lives_freep((void **)&resaudw);
4710         return FALSE;
4711       }
4712     } else {
4713       if (mainw->multitrack) rendaud = mainw->multitrack->opts.render_audp;
4714       else rendaud = prefs->render_audio;
4715       // TODO: prompt just for clip name
4716     }
4717 
4718     if (!(prefs->rec_opts & REC_AUDIO)) rendaud = FALSE;
4719 
4720     // create new file
4721     mainw->current_file = mainw->first_free_file;
4722 
4723     if (!get_new_handle(mainw->current_file, clipname)) {
4724       mainw->current_file = current_file;
4725 
4726       if (prefs->mt_enter_prompt) {
4727         if (!transcode) lives_free(rdet->encoder_name);
4728         lives_freep((void **)&rdet);
4729         lives_freep((void **)&resaudw);
4730       }
4731       lives_free(clipname);
4732       return FALSE; // show dialog again
4733     }
4734 
4735     lives_freep((void **)&clipname);
4736 
4737     cfile->opening = TRUE; // prevent audio from getting clobbered, it will be reset during rendering
4738 
4739     if (weed_plant_has_leaf(mainw->event_list, WEED_LEAF_FPS))
4740       old_fps = weed_get_double_value(mainw->event_list, WEED_LEAF_FPS, NULL);
4741 
4742     if (prefs->render_prompt) {
4743       cfile->hsize = rdet->width;
4744       cfile->vsize = rdet->height;
4745       cfile->pb_fps = cfile->fps = rdet->fps;
4746       cfile->ratio_fps = rdet->ratio_fps;
4747 
4748       cfile->arps = cfile->arate = xarate;
4749       cfile->achans = xachans;
4750       cfile->asampsize = xasamps;
4751       cfile->signed_endian = xse;
4752 
4753       if (!transcode) lives_free(rdet->encoder_name);
4754       lives_freep((void **)&rdet);
4755       lives_freep((void **)&resaudw);
4756     } else {
4757       cfile->hsize = prefs->mt_def_width;
4758       cfile->vsize = prefs->mt_def_height;
4759       cfile->pb_fps = cfile->fps = prefs->mt_def_fps;
4760       cfile->ratio_fps = FALSE;
4761       cfile->arate = cfile->arps = prefs->mt_def_arate;
4762       cfile->achans = prefs->mt_def_achans;
4763       cfile->asampsize = prefs->mt_def_asamps;
4764       cfile->signed_endian = prefs->mt_def_signed_endian;
4765     }
4766 
4767     if (old_fps != 0.) {
4768       cfile->pb_fps = cfile->fps = old_fps;
4769       cfile->ratio_fps = FALSE;
4770     }
4771 
4772     if (!rendaud) cfile->achans = cfile->arate = cfile->asampsize = 0;
4773 
4774     cfile->bpp = cfile->img_type == IMG_TYPE_JPEG ? 24 : 32;
4775     cfile->is_loaded = TRUE;
4776     if (prefs->btgamma) {
4777       if (IS_VALID_CLIP(current_file)) cfile->gamma_type = mainw->files[current_file]->gamma_type;
4778     }
4779     show_playbar_labels(mainw->current_file);
4780   } else if (!mainw->multitrack) {
4781     // back up audio to audio.back (in case we overwrite it)
4782     if (rendaud) {
4783       do_threaded_dialog(_("Backing up audio..."), FALSE);
4784       com = lives_strdup_printf("%s backup_audio \"%s\"", prefs->backend_sync, cfile->handle);
4785       mainw->error = FALSE;
4786       mainw->cancelled = CANCEL_NONE;
4787       lives_rm(cfile->info_file);
4788       lives_system(com, FALSE);
4789       lives_free(com);
4790       if (THREADVAR(com_failed)) return FALSE;
4791     } else {
4792       do_threaded_dialog(_("Clearing up clip..."), FALSE);
4793       com = lives_strdup_printf("%s clear_tmp_files \"%s\"", prefs->backend_sync, cfile->handle);
4794       lives_system(com, FALSE);
4795       lives_free(com);
4796     }
4797     end_threaded_dialog();
4798   }
4799 
4800   if (mainw->event_list && (!mainw->multitrack || mainw->unordered_blocks)) {
4801     if (old_fps == 0) {
4802       weed_plant_t *qevent_list = quantise_events(mainw->event_list, cfile->fps, !new_clip);
4803       if (qevent_list) {
4804         event_list_replace_events(mainw->event_list, qevent_list);
4805         weed_set_double_value(mainw->event_list, WEED_LEAF_FPS, cfile->fps);
4806       }
4807     }
4808   }
4809 
4810   cfile->old_frames = cfile->frames;
4811   cfile->changed = TRUE;
4812   mainw->effects_paused = FALSE;
4813 
4814   if (new_clip) cfile->img_type = IMG_TYPE_BEST; // override the pref
4815   mainw->vfade_in_secs = mainw->vfade_out_secs = 0.;
4816 
4817 #ifdef LIBAV_TRANSCODE
4818   if (transcode) {
4819     if (!transcode_prep()) {
4820       close_current_file(current_file);
4821       return FALSE;
4822     }
4823 
4824     if (!transcode_get_params(&pname)) {
4825       transcode_cleanup(mainw->vpp);
4826       close_current_file(current_file);
4827       return FALSE;
4828     }
4829 
4830     cfile->nopreview = TRUE;
4831 
4832     mainw->transrend_layer = NULL;
4833     mainw->transrend_ready = FALSE;
4834 
4835     mainw->transrend_proc = lives_proc_thread_create(LIVES_THRDATTR_NONE,
4836                             (lives_funcptr_t)transcode_clip,
4837                             WEED_SEED_BOOLEAN, "iibV", 1, 0, TRUE, pname);
4838     lives_proc_thread_set_cancellable(mainw->transrend_proc);
4839     lives_nanosleep_until_nonzero(mainw->transrend_ready);
4840 
4841     if (rendaud) {
4842       // pre-render audio
4843       d_print(_("Pre-rendering audio..."));
4844       if (!start_render_effect_events(mainw->event_list, FALSE, TRUE)) {
4845         mainw->transrend_ready = FALSE;
4846         lives_proc_thread_cancel(mainw->transrend_proc);
4847         close_current_file(current_file);
4848         retval = FALSE;
4849         goto rtc_done;
4850       }
4851       if (norm_after) on_normalise_audio_activate(NULL, NULL);
4852       if (afade_in_secs > 0.) {
4853         cfile->undo1_int = 0; // fade in
4854         cfile->undo2_dbl = 0.;
4855         cfile->undo1_dbl = afade_in_secs;
4856         on_fade_audio_activate(NULL, NULL);
4857       }
4858       if (afade_out_secs > 0.) {
4859         cfile->undo1_int = 1; // fade out
4860         cfile->undo2_dbl = cfile->laudio_time - afade_out_secs;
4861         cfile->undo1_dbl = cfile->laudio_time;
4862         on_fade_audio_activate(NULL, NULL);
4863       }
4864       d_print_done();
4865       rendaud = FALSE;
4866     }
4867 #ifdef VFADE_RENDER
4868     // temp fix until a better system emerges
4869     if (vfade_in_secs > 0.) {
4870       mainw->vfade_in_secs = vfade_in_secs;
4871       mainw->vfade_in_col = vfade_rgb;
4872     }
4873     // temp fix until a better system emerges
4874     if (vfade_out_secs > 0.) {
4875       mainw->vfade_out_secs = vfade_out_secs;
4876       mainw->vfade_out_col = vfade_rgb;
4877     }
4878 #endif
4879     mainw->transrend_ready = FALSE;
4880   }
4881 #endif
4882 
4883   if (mainw->multitrack && !rendaud && !mainw->multitrack->opts.render_vidp) {
4884     return FALSE;
4885   }
4886 
4887   if (!transcode) d_print(_("Rendering..."));
4888   else d_print(_("Transcoding..."));
4889 
4890   init_track_decoders();
4891 
4892   if (transcode) {
4893     cfile->progress_start = 0;
4894     cfile->progress_end = cfile->frames;
4895   }
4896 
4897   if (start_render_effect_events(mainw->event_list, TRUE, rendaud)) { // re-render, applying effects
4898     // and reordering/resampling/resizing if necessary
4899     if (!transcode) {
4900       if (!mainw->multitrack && mainw->event_list) {
4901         if (!new_clip) {
4902           // this is needed in case we render to same clip, and then undo ///////
4903           if (cfile->event_list_back) event_list_free(cfile->event_list_back);
4904           cfile->event_list_back = mainw->event_list;
4905           ///////////////////////////////////////////////////////////////////////
4906         } else event_list_free(mainw->event_list);
4907       }
4908       mainw->event_list = NULL;
4909     }
4910     if (mainw->scrap_pixbuf) {
4911       lives_widget_object_unref(mainw->scrap_pixbuf);
4912       mainw->scrap_pixbuf = NULL;
4913     }
4914     if (new_clip) {
4915       char *tmp;
4916       int old_file = current_file;
4917 
4918       if (transcode) {
4919         mainw->transrend_ready = TRUE;
4920         lives_proc_thread_cancel(mainw->transrend_proc);
4921         mainw->transrend_proc = NULL;
4922         close_current_file(old_file);
4923         goto rtc_done;
4924       }
4925 
4926       if (rendaud && norm_after) on_normalise_audio_activate(NULL, NULL);
4927 
4928       cfile->start = 1;
4929       cfile->end = cfile->frames;
4930 
4931       set_undoable(NULL, FALSE);
4932       add_to_clipmenu();
4933       current_file = mainw->current_file;
4934       if (!save_clip_values(current_file)) {
4935         close_current_file(old_file);
4936         d_print_failed();
4937         retval = FALSE;
4938         goto rtc_done;
4939       }
4940       if (prefs->crash_recovery) add_to_recovery_file(cfile->handle);
4941       if (!mainw->multitrack) {
4942         switch_clip(1, current_file, TRUE);
4943       }
4944       d_print((tmp = lives_strdup_printf(_("rendered %d frames to new clip.\n"), cfile->frames)));
4945       lives_free(tmp);
4946       mainw->pre_src_file = mainw->current_file; // if a generator started playback, we will switch back to this file after
4947       lives_notify(LIVES_OSC_NOTIFY_CLIP_OPENED, "");
4948     } else {
4949       // rendered to same clip - update number of frames
4950       if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FRAMES, &cfile->frames))
4951         do_header_write_error(mainw->current_file);
4952     }
4953 
4954     if (cfile->clip_type == CLIP_TYPE_FILE) {
4955       if (cfile->undo_start == 1 && cfile->undo_end == cfile->frames) {
4956         cfile->clip_type = CLIP_TYPE_DISK;
4957         lives_freep((void **)&cfile->frame_index_back);
4958         cfile->frame_index_back = cfile->frame_index;  // save for undo :: TODO
4959         del_frame_index(cfile);
4960       } else {
4961         char *what = (_("a new file index"));
4962         LiVESResponseType response;
4963         lives_freep((void **)&cfile->frame_index_back);
4964 
4965         do {
4966           response = LIVES_RESPONSE_OK;
4967           cfile->frame_index_back = cfile->frame_index;  // save for undo :: TODO
4968           cfile->frame_index = NULL;
4969           create_frame_index(mainw->current_file, FALSE, 0, cfile->frames);
4970           if (!cfile->frame_index) {
4971             cfile->frame_index = cfile->frame_index_back;
4972             cfile->frame_index_back = NULL;
4973             response = do_memory_error_dialog(what, cfile->frames * 4);
4974           }
4975         } while (response == LIVES_RESPONSE_RETRY);
4976         lives_free(what);
4977 
4978         if (response == LIVES_RESPONSE_CANCEL) {
4979           if (!mainw->multitrack) {
4980             if (new_clip) { // check
4981               close_current_file(current_file);
4982             } else {
4983               cfile->frame_index = cfile->frame_index_back;
4984               cfile->frame_index_back = NULL;
4985             }
4986           }
4987           return FALSE; /// will reshow the dialog
4988         }
4989 
4990         lives_memcpy(cfile->frame_index, cfile->frame_index_back, cfile->undo_start * sizeof(frames_t));
4991 
4992         for (int i = cfile->undo_start - 1; i < cfile->undo_end; i++) {
4993           cfile->frame_index[i] = -1;
4994         }
4995 
4996         lives_memcpy(&cfile->frame_index[cfile->undo_end], &cfile->frame_index_back[cfile->undo_end],
4997                      (cfile->frames - cfile->undo_end) * sizeof(frames_t));
4998 
4999         save_frame_index(mainw->current_file);
5000       }
5001     }
5002     if (!new_clip) d_print_done();
5003   } else {
5004     retval = FALSE; // cancelled or error, so show the dialog again
5005     if (transcode) {
5006       mainw->transrend_ready = TRUE;
5007       lives_proc_thread_cancel(mainw->transrend_proc);
5008       mainw->transrend_proc = NULL;
5009     }
5010     if (transcode || (new_clip && !mainw->multitrack)) {
5011       // for mt we are rendering to the actual mt file, so we cant close it (CHECK: did we delete all images ?)
5012       close_current_file(current_file);
5013     }
5014   }
5015 
5016 rtc_done:
5017   mainw->effects_paused = FALSE;
5018   free_track_decoders();
5019   deinit_render_effects();
5020   audio_free_fnames();
5021   mainw->vfade_in_secs = mainw->vfade_out_secs = 0.;
5022   return retval;
5023 }
5024 
5025 
dprint_recneg(void)5026 LIVES_INLINE void dprint_recneg(void) {d_print(_("nothing recorded.\n"));}
5027 
backup_recording(char ** esave_file,char ** asave_file)5028 boolean backup_recording(char **esave_file, char **asave_file) {
5029   char *x, *y;
5030   LiVESList *clist = mainw->cliplist;
5031   double vald = 0.;
5032   int fd, i, hdlsize;
5033 
5034   if (!esave_file) esave_file = &x;
5035   if (!asave_file) asave_file = &y;
5036 
5037   *esave_file = lives_strdup_printf("%s/recorded-%s.%d.%d.%d.%s", prefs->workdir, LAYOUT_FILENAME, lives_getuid(), lives_getgid(),
5038                                     capable->mainpid, LIVES_FILE_EXT_LAYOUT);
5039   THREADVAR(write_failed) = FALSE;
5040   fd = lives_create_buffered(*esave_file, DEF_FILE_PERMS);
5041   if (fd >= 0) {
5042     save_event_list_inner(NULL, fd, mainw->event_list, NULL);
5043     lives_close_buffered(fd);
5044   }
5045   if (fd < 0 || THREADVAR(write_failed)) {
5046     if (mainw->is_exiting) return FALSE;
5047     THREADVAR(write_failed) = FALSE;
5048     lives_freep((void **)esave_file);
5049     *asave_file = NULL;
5050     return FALSE;
5051   }
5052 
5053   *asave_file = lives_strdup_printf("%s/recorded-%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME, lives_getuid(),
5054                                     lives_getgid(),
5055                                     capable->mainpid);
5056 
5057   fd = lives_create_buffered(*asave_file, DEF_FILE_PERMS);
5058   if (fd >= 0) {
5059     while (!THREADVAR(write_failed) && clist) {
5060       i = LIVES_POINTER_TO_INT(clist->data);
5061       if (IS_NORMAL_CLIP(i)) {
5062         lives_write_le_buffered(fd, &i, 4, TRUE);
5063         lives_write_le_buffered(fd, &vald, 8, TRUE);
5064         hdlsize = strlen(mainw->files[i]->handle);
5065         lives_write_le_buffered(fd, &hdlsize, 4, TRUE);
5066         lives_write_buffered(fd, (const char *)&mainw->files[i]->handle, hdlsize, TRUE);
5067       }
5068       clist = clist->next;
5069     }
5070     lives_close_buffered(fd);
5071   }
5072 
5073   if (fd < 0 || THREADVAR(write_failed)) {
5074     if (mainw->is_exiting) return FALSE;
5075     THREADVAR(write_failed) = FALSE;
5076     lives_rm(*esave_file);
5077     if (fd >= 0) lives_rm(*asave_file);
5078     lives_freep((void **)esave_file);
5079     lives_freep((void **)asave_file);
5080     return FALSE;
5081   }
5082   return TRUE;
5083 }
5084 
5085 
_show_rc_dlg(void)5086 static LiVESResponseType _show_rc_dlg(void) {
5087   LiVESResponseType resp;
5088   LiVESWidget *e_rec_dialog = events_rec_dialog();
5089   resp = lives_dialog_run(LIVES_DIALOG(e_rec_dialog));
5090   lives_widget_destroy(e_rec_dialog);
5091   return resp;
5092 }
5093 
5094 
show_rc_dlg(void)5095 static LiVESResponseType show_rc_dlg(void) {
5096   LiVESResponseType resp;
5097   main_thread_execute((lives_funcptr_t)_show_rc_dlg, WEED_SEED_INT, &resp, "");
5098   return resp;
5099 }
5100 
5101 
event_list_add_end_events(weed_event_t * event_list,boolean is_final)5102 void event_list_add_end_events(weed_event_t *event_list, boolean is_final) {
5103   // for realtime recording, add filter deinit events and switch off audio
5104   // this occurs either when recording is paused (is_final == FALSE) or when playback ends (is_final == TRUE)
5105   if (event_list) {
5106     pthread_mutex_t *event_list_mutex = NULL;
5107     if (event_list == mainw->event_list) event_list_mutex = &mainw->event_list_mutex;
5108 
5109     if (prefs->rec_opts & REC_EFFECTS) {
5110       // add deinit events for all active effects
5111       // this will lock the event _list itself
5112       add_filter_deinit_events(event_list);
5113     }
5114 
5115     if (is_final) {
5116       // switch audio off
5117 #ifdef RT_AUDIO
5118       if (mainw->record && !mainw->record_paused && (prefs->rec_opts & REC_AUDIO)
5119           && mainw->agen_key == 0 && !mainw->agen_needs_reinit &&
5120           prefs->audio_src == AUDIO_SRC_INT) {
5121         if (!mainw->mute) {
5122           weed_plant_t *last_frame = get_last_event(event_list);
5123           event_list = insert_blank_frame_event_at(event_list, mainw->currticks, &last_frame);
5124           if (last_frame) {
5125 #ifdef ENABLE_JACK
5126             if (prefs->audio_player == AUD_PLAYER_JACK) {
5127               if (mainw->jackd)
5128                 jack_get_rec_avals(mainw->jackd);
5129             }
5130 #endif
5131 #ifdef HAVE_PULSE_AUDIO
5132             if (prefs->audio_player == AUD_PLAYER_PULSE) {
5133               if (mainw->pulsed)
5134                 pulse_get_rec_avals(mainw->pulsed);
5135             }
5136 #endif
5137 #if 0
5138             if (prefs->audio_player == AUD_PLAYER_NONE) {
5139               nullaudio_get_rec_avals();
5140             }
5141 #endif
5142             insert_audio_event_at(last_frame, -1, mainw->rec_aclip, mainw->rec_aseek, 0.);
5143 	    // *INDENT-OFF*
5144 	  }}}
5145       // *INDENT-ON*
5146 #endif
5147     } else {
5148       // write a RECORD_END marker
5149       weed_timecode_t tc;
5150       if (event_list_mutex) pthread_mutex_lock(event_list_mutex);
5151       tc = get_event_timecode(get_last_event(event_list));
5152       event_list = append_marker_event(event_list, tc, EVENT_MARKER_RECORD_END); // mark record end
5153       if (event_list_mutex) pthread_mutex_unlock(event_list_mutex);
5154     }
5155   }
5156 }
5157 
5158 
deal_with_render_choice(boolean add_deinit)5159 boolean deal_with_render_choice(boolean add_deinit) {
5160   // this is called from saveplay.c after record/playback ends
5161   // here we deal with the user's wishes as to how to deal with the recorded events
5162 
5163   // mainw->osc_block should be TRUE during all of this, so we don't have to contend with
5164   // any incoming network messages
5165 
5166   // return TRUE if we rendered to a new clip
5167   lives_proc_thread_t info = NULL;
5168 
5169   LiVESWidget *elist_dialog;
5170 
5171   double df;
5172 
5173   char *esave_file = NULL, *asave_file = NULL;
5174 
5175   boolean new_clip = FALSE, transcode;
5176 
5177   int dh, dw, dar, das, dac, dse;
5178   int oplay_start;
5179 
5180   render_choice = RENDER_CHOICE_NONE;
5181 
5182   if (!CURRENT_CLIP_IS_VALID) {
5183     /// user may have recorded a  generator with no other clips loaded
5184     if (mainw->scrap_file != -1)
5185       mainw->current_file = mainw->scrap_file;
5186     else if (mainw->ascrap_file != -1)
5187       mainw->current_file = mainw->ascrap_file;
5188     if (CURRENT_CLIP_IS_VALID) {
5189       cfile->hsize = DEF_GEN_WIDTH;
5190       cfile->vsize = DEF_GEN_HEIGHT;
5191     }
5192   }
5193 
5194   if (!CURRENT_CLIP_IS_VALID) render_choice = RENDER_CHOICE_MULTITRACK;
5195 
5196   if (count_events(mainw->event_list, FALSE, 0, 0) == 0) {
5197     event_list_free(mainw->event_list);
5198     mainw->event_list = NULL;
5199   }
5200 
5201   if (!mainw->event_list) {
5202     close_scrap_file(TRUE);
5203     close_ascrap_file(TRUE);
5204     dprint_recneg();
5205     return FALSE;
5206   }
5207 
5208   last_rec_start_tc = -1;
5209 
5210   event_list_close_gaps(mainw->event_list);
5211 
5212   // need to retain play_start for rendering to same clip
5213   oplay_start = mainw->play_start;
5214 
5215   check_storage_space(-1, FALSE);
5216 
5217   if (prefs->gui_monitor == 0) {
5218     // avoid an annoyance
5219     pref_factory_int(PREF_SEPWIN_TYPE, SEPWIN_TYPE_NON_STICKY, FALSE);
5220   }
5221 
5222   // crash recovery -> backup the event list
5223   if (prefs->crash_recovery && prefs->rr_crash) {
5224     info = lives_proc_thread_create(LIVES_THRDATTR_NO_GUI, (lives_funcptr_t)backup_recording, -1, "vv",
5225                                     &esave_file, &asave_file);
5226   }
5227 
5228   do {
5229     transcode = FALSE;
5230     if (render_choice == RENDER_CHOICE_NONE || render_choice == RENDER_CHOICE_PREVIEW)
5231       if (show_rc_dlg() == LIVES_RESPONSE_CANCEL) render_choice = RENDER_CHOICE_DISCARD;
5232     switch (render_choice) {
5233     case RENDER_CHOICE_DISCARD:
5234       if ((mainw->current_file == mainw->scrap_file || mainw->current_file == mainw->ascrap_file)
5235           && !mainw->clips_available) {
5236         mainw->current_file = -1;
5237         lives_ce_update_timeline(0, 0.);
5238       } else if (CURRENT_CLIP_IS_VALID) cfile->redoable = FALSE;
5239       close_scrap_file(TRUE);
5240       close_ascrap_file(TRUE);
5241       sensitize();
5242       break;
5243     case RENDER_CHOICE_PREVIEW:
5244       // preview
5245       cfile->next_event = get_first_event(mainw->event_list);
5246       mainw->is_rendering = TRUE;
5247       mainw->preview_rendering = TRUE;
5248       if (prefs->audio_src == AUDIO_SRC_EXT) {
5249         pref_factory_bool(PREF_REC_EXT_AUDIO, FALSE, FALSE);
5250       }
5251       on_preview_clicked(NULL, NULL);
5252       if (future_prefs->audio_src == AUDIO_SRC_EXT) {
5253         pref_factory_bool(PREF_REC_EXT_AUDIO, TRUE, FALSE);
5254       }
5255       free_track_decoders();
5256       deinit_render_effects();
5257       mainw->preview_rendering = FALSE;
5258       mainw->is_processing = mainw->is_rendering = FALSE;
5259       cfile->next_event = NULL;
5260       break;
5261     case RENDER_CHOICE_TRANSCODE:
5262       transcode = TRUE;
5263     case RENDER_CHOICE_NEW_CLIP:
5264       dw = prefs->mt_def_width;
5265       dh = prefs->mt_def_height;
5266       df = prefs->mt_def_fps;
5267       dar = prefs->mt_def_arate;
5268       dac = prefs->mt_def_achans;
5269       das = prefs->mt_def_asamps;
5270       dse = prefs->mt_def_signed_endian;
5271       if (!mainw->clip_switched && prefs->render_prompt && mainw->current_file > -1) {
5272         if (cfile->hsize > 0) prefs->mt_def_width = cfile->hsize;
5273         if (cfile->vsize > 0) prefs->mt_def_height = cfile->vsize;
5274         prefs->mt_def_fps = cfile->fps;
5275         if (cfile->achans * cfile->arate * cfile->asampsize > 0) {
5276           prefs->mt_def_arate = cfile->arate;
5277           prefs->mt_def_asamps = cfile->asampsize;
5278           prefs->mt_def_achans = cfile->achans;
5279           prefs->mt_def_signed_endian = cfile->signed_endian;
5280         }
5281       }
5282       mainw->play_start = 1; ///< new clip frames always start  at 1
5283       if (info) {
5284         //lives_nanosleep_until_nonzero(weed_get_boolean_value(info, WEED_LEAF_DONE, NULL));
5285         lives_proc_thread_join(info);
5286         info = NULL;
5287       }
5288       if (!render_to_clip(TRUE, transcode) || render_choice == RENDER_CHOICE_TRANSCODE)
5289         render_choice = RENDER_CHOICE_PREVIEW;
5290       else {
5291         close_scrap_file(TRUE);
5292         close_ascrap_file(TRUE);
5293         prefs->mt_def_width = dw;
5294         prefs->mt_def_height = dh;
5295         prefs->mt_def_fps = df;
5296         prefs->mt_def_arate = dar;
5297         prefs->mt_def_achans = dac;
5298         prefs->mt_def_asamps = das;
5299         prefs->mt_def_signed_endian = dse;
5300         mainw->is_rendering = FALSE;
5301         new_clip = TRUE;
5302       }
5303       break;
5304     case RENDER_CHOICE_SAME_CLIP:
5305       cfile->undo_start = mainw->play_start = oplay_start; ///< same clip frames start where recording started
5306       if (info) {
5307         lives_proc_thread_join(info);
5308         info = NULL;
5309       }
5310       if (!render_to_clip(FALSE, FALSE)) render_choice = RENDER_CHOICE_PREVIEW;
5311       else {
5312         close_scrap_file(TRUE);
5313         close_ascrap_file(TRUE);
5314       }
5315       mainw->is_rendering = FALSE;
5316       break;
5317     case RENDER_CHOICE_MULTITRACK:
5318       if (mainw->stored_event_list && mainw->stored_event_list_changed) {
5319         if (!check_for_layout_del(NULL, FALSE)) {
5320           render_choice = RENDER_CHOICE_PREVIEW;
5321           break;
5322         }
5323       }
5324       if (mainw->stored_event_list || mainw->sl_undo_mem) {
5325         recover_layout_cancelled(FALSE);
5326         stored_event_list_free_all(TRUE);
5327       }
5328       mainw->unordered_blocks = TRUE;
5329       pref_factory_int(PREF_SEPWIN_TYPE, future_prefs->sepwin_type, FALSE);
5330       if (info) {
5331         lives_proc_thread_join(info);
5332         info = NULL;
5333       }
5334       prefs->letterbox_mt = prefs->letterbox;
5335       if (on_multitrack_activate(NULL, (weed_plant_t *)mainw->event_list)) {
5336         prefs->letterbox_mt = future_prefs->letterbox_mt;
5337         mainw->event_list = NULL;
5338         new_clip = TRUE;
5339       } else render_choice = RENDER_CHOICE_PREVIEW;
5340       mainw->unordered_blocks = FALSE;
5341       break;
5342     case RENDER_CHOICE_EVENT_LIST:
5343       if (count_events(mainw->event_list, prefs->event_window_show_frame_events, 0, 0) > 1000) {
5344         if (!do_event_list_warning()) {
5345           render_choice = RENDER_CHOICE_PREVIEW;
5346           break;
5347         }
5348       }
5349       elist_dialog = create_event_list_dialog(mainw->event_list, 0, 0);
5350       lives_dialog_run(LIVES_DIALOG(elist_dialog));
5351       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
5352       lives_widget_context_update();
5353       render_choice = RENDER_CHOICE_PREVIEW;
5354       break;
5355     }
5356 
5357     if (CURRENT_CLIP_IS_VALID) cfile->next_event = NULL;
5358 
5359     if (IS_VALID_CLIP(mainw->scrap_file)) {
5360       // rewind scrap file to beginning
5361       if (!mainw->files[mainw->scrap_file]->ext_src) load_from_scrap_file(NULL, -1);
5362       lives_lseek_buffered_rdonly_absolute(LIVES_POINTER_TO_INT(mainw->files[mainw->scrap_file]->ext_src), 0);
5363     }
5364   } while (render_choice == RENDER_CHOICE_PREVIEW);
5365 
5366   if (info) {
5367     lives_proc_thread_join(info);
5368     info = NULL;
5369   }
5370 
5371   if (esave_file) lives_rm(esave_file);
5372   if (asave_file) lives_rm(asave_file);
5373 
5374   if (mainw->event_list) {
5375     event_list_free(mainw->event_list);
5376     mainw->event_list = NULL;
5377   }
5378 
5379   /// multitrack will set this itself
5380   if (render_choice != RENDER_CHOICE_MULTITRACK)
5381     pref_factory_int(PREF_SEPWIN_TYPE, future_prefs->sepwin_type, FALSE);
5382 
5383   sensitize();
5384 
5385   check_storage_space(mainw->current_file, FALSE);
5386 
5387   return new_clip;
5388 }
5389 
5390 
5391 /**
5392    @brief calculate the "visibility" of each track at timecode tc
5393 
5394      that is to say, only the front track is visible, except if we have a transition and WEED_LEAF_HOST_AUDIO_TRANSITION is set
5395      - in which case the track visibilty is proportional to the transition parameter
5396 
5397      to do this, we need a filter map and a frame/clip stack
5398 
5399      if bleedthru is TRUE, all values are set to 1.0 */
get_track_visibility_at_tc(weed_plant_t * event_list,int ntracks,int nbtracks,weed_timecode_t tc,weed_plant_t ** shortcut,boolean bleedthru)5400 double *get_track_visibility_at_tc(weed_plant_t *event_list, int ntracks, int nbtracks,
5401                                    weed_timecode_t tc, weed_plant_t **shortcut, boolean bleedthru) {
5402   static weed_plant_t *stored_fmap;
5403 
5404   weed_plant_t *frame_event, *fmap;
5405 
5406   double *vis;
5407   double *matrix[ntracks + nbtracks];
5408 
5409   int *clips = NULL;
5410   int64_t *frames = NULL;
5411 
5412   int nxtracks;
5413   int got = -1;
5414 
5415   register int i, j;
5416 
5417   ntracks += nbtracks;
5418 
5419   if (!shortcut || !*shortcut) stored_fmap = NULL;
5420 
5421   if (shortcut) *shortcut = frame_event = get_frame_event_at_or_before(event_list, tc, *shortcut);
5422   else frame_event = get_frame_event_at_or_before(event_list, tc, NULL);
5423 
5424   nxtracks = weed_leaf_num_elements(frame_event, WEED_LEAF_CLIPS);
5425 
5426   vis = (double *)lives_malloc(ntracks * sizeof(double));
5427 
5428   if (bleedthru) {
5429     for (i = 0; i < ntracks; i++) {
5430       vis[i] = 1.;
5431     }
5432     return vis;
5433   }
5434 
5435   clips = weed_get_int_array(frame_event, WEED_LEAF_CLIPS, NULL);
5436   frames = weed_get_int64_array(frame_event, WEED_LEAF_FRAMES, NULL);
5437 
5438   if (nbtracks > 0) vis[0] = 1.;
5439 
5440   if (!stored_fmap) stored_fmap = fmap = get_filter_map_before(frame_event, LIVES_TRACK_ANY, NULL);
5441   else {
5442     fmap = get_filter_map_before(frame_event, LIVES_TRACK_ANY, *shortcut);
5443     if (fmap == *shortcut) fmap = stored_fmap;
5444   }
5445 
5446   for (i = 0; i < ntracks; i++) {
5447     matrix[i] = (double *)lives_malloc(ntracks * sizeof(double));
5448     for (j = 0; j < ntracks; j++) {
5449       matrix[i][j] = 0.;
5450     }
5451     matrix[i][i] = 1.;
5452   }
5453 
5454   if (fmap) {
5455     // here we look at all init_events in fmap. If any have WEED_LEAF_HOST_AUDIO_TRANSITION set, then
5456     // we we look at the 2 in channels. We first multiply matrix[t0][...] by trans - 1
5457     // then we add matrix[t1][...] * (trans) to matrix[t3][...]
5458     // where trans is the normalised value of the transition parameter
5459     // t3 is the output channel, (this is usually the same track as t0)
5460     // thus each row in the matrix represents the contribution from each layer (track)
5461     if (weed_plant_has_leaf(fmap, WEED_LEAF_INIT_EVENTS)) {
5462       int nins;
5463       weed_plant_t **iev = (weed_plant_t **)weed_get_voidptr_array_counted(fmap, WEED_LEAF_INIT_EVENTS, &nins);
5464       for (i = 0; i < nins; i++) {
5465         weed_plant_t *ievent = iev[i];
5466         if (weed_get_boolean_value(ievent, WEED_LEAF_HOST_AUDIO_TRANSITION, NULL) == WEED_TRUE) {
5467           int *in_tracks = weed_get_int_array(ievent, WEED_LEAF_IN_TRACKS, NULL);
5468           int *out_tracks = weed_get_int_array(ievent, WEED_LEAF_OUT_TRACKS, NULL);
5469           char *filter_hash = weed_get_string_value(ievent, WEED_LEAF_FILTER, NULL);
5470           int idx;
5471           if ((idx = weed_get_idx_for_hashname(filter_hash, TRUE)) != -1) {
5472             int npch;
5473             weed_plant_t *filter = get_weed_filter(idx);
5474             int tparam = get_transition_param(filter, FALSE);
5475             weed_plant_t *inst = weed_instance_from_filter(filter);
5476             weed_plant_t **in_params = weed_instance_get_in_params(inst, NULL);
5477             weed_plant_t *ttmpl = weed_param_get_template(in_params[tparam]);
5478             void **pchains = weed_get_voidptr_array_counted(ievent, WEED_LEAF_IN_PARAMETERS, &npch);
5479             double trans;
5480 
5481             if (tparam < npch) interpolate_param(in_params[tparam], pchains[tparam], tc);
5482             lives_free(pchains);
5483 
5484             if (weed_leaf_seed_type(in_params[tparam], WEED_LEAF_VALUE) == WEED_SEED_DOUBLE) {
5485               double transd = weed_get_double_value(in_params[tparam], WEED_LEAF_VALUE, NULL);
5486               double tmin = weed_get_double_value(ttmpl, WEED_LEAF_MIN, NULL);
5487               double tmax = weed_get_double_value(ttmpl, WEED_LEAF_MAX, NULL);
5488               trans = (transd - tmin) / (tmax - tmin);
5489             } else {
5490               int transi = weed_get_int_value(in_params[tparam], WEED_LEAF_VALUE, NULL);
5491               int tmin = weed_get_int_value(ttmpl, WEED_LEAF_MIN, NULL);
5492               int tmax = weed_get_int_value(ttmpl, WEED_LEAF_MAX, NULL);
5493               trans = (double)(transi - tmin) / (double)(tmax - tmin);
5494             }
5495             lives_free(in_params);
5496             for (j = 0; j < ntracks; j++) {
5497               /// TODO *** make selectable: linear or non-linear
5498               /* matrix[in_tracks[1] + nbtracks][j] *= lives_vol_from_linear(trans); */
5499               /* matrix[in_tracks[0] + nbtracks][j] *= lives_vol_from_linear((1. - trans)); */
5500               matrix[in_tracks[1] + nbtracks][j] *= trans;
5501               matrix[in_tracks[0] + nbtracks][j] *= 1. - trans;
5502               matrix[out_tracks[0] + nbtracks][j] = matrix[in_tracks[0] + nbtracks][j] + matrix[in_tracks[1] + nbtracks][j];
5503             }
5504 
5505             weed_instance_unref(inst);
5506             weed_instance_unref(inst);
5507           }
5508           lives_free(in_tracks);
5509           lives_free(out_tracks);
5510           lives_free(filter_hash);
5511         }
5512       }
5513       lives_free(iev);
5514     }
5515   }
5516 
5517   // now we select as visibility, whichever row is the first layer to have a non-blank frame
5518 
5519   for (i = 0; i < nxtracks; i++) {
5520     if (clips[i] >= 0 && frames[i] > 0) {
5521       got = i + nbtracks;
5522       break;
5523     }
5524   }
5525   lives_free(clips);
5526   lives_free(frames);
5527 
5528   if (got == -1) {
5529     // all frames blank - backing audio only
5530     for (i = 0; i < ntracks; i++) {
5531       if (i >= nbtracks) vis[i] = 0.;
5532       lives_free(matrix[i]);
5533     }
5534     return vis;
5535   }
5536 
5537   for (i = nbtracks; i < ntracks; i++) {
5538     vis[i] = matrix[got][i];
5539   }
5540 
5541   for (i = 0; i < ntracks; i++) {
5542     lives_free(matrix[i]);
5543   }
5544 
5545   return vis;
5546 }
5547 
5548 //////////////////////
5549 //GUI stuff
5550 
5551 enum {
5552   TITLE_COLUMN,
5553   KEY_COLUMN,
5554   VALUE_COLUMN,
5555   DESC_COLUMN,
5556   NUM_COLUMNS
5557 };
5558 
5559 
5560 #if GTK_CHECK_VERSION(3, 0, 0)
rowexpand(LiVESWidget * tv,LiVESTreeIter * iter,LiVESTreePath * path,livespointer ud)5561 static void rowexpand(LiVESWidget * tv, LiVESTreeIter * iter, LiVESTreePath * path, livespointer ud) {
5562   lives_widget_queue_resize(tv);
5563   lives_widget_queue_draw(tv);
5564   lives_widget_process_updates(tv);
5565 }
5566 #endif
5567 
5568 
quant_clicked(LiVESButton * button,livespointer elist)5569 static void quant_clicked(LiVESButton * button, livespointer elist) {
5570   weed_plant_t *ev_list = (weed_plant_t *)elist;
5571   weed_plant_t *qevent_list = quantise_events(ev_list, cfile->fps, FALSE);
5572   if (qevent_list) {
5573     /* reset_renumbering(); */
5574     /* event_list_rectify(NULL, qevent_list); */
5575     event_list_replace_events(ev_list, qevent_list);
5576     weed_set_double_value(ev_list, WEED_LEAF_FPS, cfile->fps);
5577   }
5578   lives_general_button_clicked(button, NULL);
5579 }
5580 
5581 
create_event_list_dialog(weed_plant_t * event_list,weed_timecode_t start_tc,weed_timecode_t end_tc)5582 LiVESWidget *create_event_list_dialog(weed_plant_t *event_list, weed_timecode_t start_tc, weed_timecode_t end_tc) {
5583   // TODO - some event properties should be editable, e.g. parameter values
5584   weed_timecode_t tc, tc_secs;
5585 
5586   LiVESTreeStore *treestore;
5587   LiVESTreeIter iter1, iter2, iter3;
5588   static size_t inistrlen = 0;
5589 
5590   char **string = NULL;
5591   int *intval = NULL;
5592   void **voidval = NULL;
5593   double *doubval = NULL;
5594   int64_t *int64val = NULL;
5595 
5596   weed_plant_t *event, *ievent;
5597 
5598   LiVESWidget *event_dialog, *daa;
5599   LiVESWidget *tree;
5600   LiVESWidget *table;
5601   LiVESWidget *top_vbox;
5602   LiVESWidget *label;
5603   LiVESWidget *ok_button;
5604   LiVESWidget *scrolledwindow;
5605 
5606   LiVESCellRenderer *renderer;
5607   LiVESTreeViewColumn *column;
5608 
5609   LiVESAccelGroup *accel_group;
5610 
5611   char **propnames;
5612 
5613   char *strval = NULL, *desc = NULL;
5614   char *text, *ltext;
5615   char *oldval = NULL, *final = NULL;
5616   char *iname = NULL, *fname = NULL;
5617   char *tmp;
5618 
5619   int woat = widget_opts.apply_theme;
5620 
5621   int winsize_h;
5622   int winsize_v;
5623 
5624   int num_elems, seed_type, etype;
5625   int rows, currow = 0;
5626   int ie_idx = 0;
5627 
5628   int i, j;
5629 
5630   if (inistrlen == 0) inistrlen = lives_strlen(WEED_LEAF_INIT_EVENT);
5631 
5632   if (prefs->event_window_show_frame_events) rows = count_events(event_list, TRUE, start_tc, end_tc);
5633   else rows = count_events(event_list, TRUE, start_tc, end_tc) - count_events(event_list, FALSE, start_tc, end_tc);
5634 
5635   //event = get_first_event(event_list);
5636   event = get_first_event(event_list);
5637 
5638   winsize_h = GUI_SCREEN_WIDTH - SCR_WIDTH_SAFETY;
5639   winsize_v = GUI_SCREEN_HEIGHT - SCR_HEIGHT_SAFETY;
5640 
5641   event_dialog = lives_standard_dialog_new(_("Event List"), FALSE, winsize_h, winsize_v);
5642 
5643   accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
5644   lives_window_add_accel_group(LIVES_WINDOW(event_dialog), accel_group);
5645 
5646   top_vbox = lives_dialog_get_content_area(LIVES_DIALOG(event_dialog));
5647 
5648   table = lives_table_new(rows, 6, FALSE);
5649   lives_widget_set_valign(table, LIVES_ALIGN_START);
5650 
5651   while (event) {
5652     // pass through all events
5653     tc = get_event_timecode(event);
5654 
5655     if (end_tc > 0) {
5656       if (tc < start_tc) {
5657         event = get_next_event(event);
5658         continue;
5659       }
5660       if (tc >= end_tc) break;
5661     }
5662 
5663     etype = get_event_type(event);
5664 
5665     if ((prefs->event_window_show_frame_events || !WEED_EVENT_IS_FRAME(event)) || WEED_EVENT_IS_AUDIO_FRAME(event)) {
5666       if (!prefs->event_window_show_frame_events && WEED_EVENT_IS_FRAME(event)) {
5667         // TODO - opts should be all frames, only audio frames, no frames
5668         // or even better, filter for any event types
5669         rows++;
5670         lives_table_resize(LIVES_TABLE(table), rows, 6);
5671       }
5672 
5673       treestore = lives_tree_store_new(NUM_COLUMNS, LIVES_COL_TYPE_STRING, LIVES_COL_TYPE_STRING,
5674                                        LIVES_COL_TYPE_STRING, LIVES_COL_TYPE_STRING);
5675 
5676       lives_tree_store_append(treestore, &iter1, NULL);   /* Acquire an iterator */
5677       lives_tree_store_set(treestore, &iter1, TITLE_COLUMN, "Properties", -1);
5678 
5679       // get list of keys (property) names for this event
5680       propnames = weed_plant_list_leaves(event, NULL);
5681 
5682       for (i = 0; propnames[i]; i++) {
5683         if (!strcmp(propnames[i], WEED_LEAF_TYPE) || !strcmp(propnames[i], WEED_LEAF_EVENT_TYPE) ||
5684             !lives_strcmp(propnames[i], WEED_LEAF_TIMECODE) || !strncmp(propnames[i], "host_", 5)) {
5685           lives_free(propnames[i]);
5686           continue;
5687         }
5688         lives_tree_store_append(treestore, &iter2, &iter1);   /* Acquire a child iterator */
5689 
5690         lives_freep((void **)&oldval);
5691         lives_freep((void **)&final);
5692 
5693         num_elems = weed_leaf_num_elements(event, propnames[i]);
5694         seed_type = weed_leaf_seed_type(event, propnames[i]);
5695 
5696         switch (seed_type) {
5697         // get the value
5698         case WEED_SEED_INT:
5699           intval = weed_get_int_array(event, propnames[i], NULL);
5700           break;
5701         case WEED_SEED_INT64:
5702           int64val = weed_get_int64_array(event, propnames[i], NULL);
5703           break;
5704         case WEED_SEED_BOOLEAN:
5705           intval = weed_get_boolean_array(event, propnames[i], NULL);
5706           break;
5707         case WEED_SEED_STRING:
5708           string = weed_get_string_array(event, propnames[i], NULL);
5709           break;
5710         case WEED_SEED_DOUBLE:
5711           doubval = weed_get_double_array(event, propnames[i], NULL);
5712           break;
5713         case WEED_SEED_VOIDPTR:
5714           voidval = weed_get_voidptr_array(event, propnames[i], NULL);
5715           break;
5716         case WEED_SEED_PLANTPTR:
5717           voidval = (void **)weed_get_plantptr_array(event, propnames[i], NULL);
5718           break;
5719         }
5720 
5721         ievent = NULL;
5722 
5723         for (j = 0; j < num_elems; j++) {
5724           if (etype == WEED_EVENT_TYPE_PARAM_CHANGE && (!strcmp(propnames[i], WEED_LEAF_INDEX))
5725               && seed_type == WEED_SEED_INT) {
5726             char *pname = NULL; // want the parameter name for the index
5727             weed_plant_t *ptmpl = NULL;
5728             ievent = (weed_plant_t *)weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
5729             if (ievent) {
5730               lives_freep((void **)&iname);
5731               iname = weed_get_string_value(ievent, WEED_LEAF_FILTER, NULL);
5732               if (iname) {
5733                 ie_idx = weed_get_idx_for_hashname(iname, TRUE);
5734               }
5735               lives_freep((void **)&iname);
5736               ptmpl = weed_filter_in_paramtmpl(get_weed_filter(ie_idx), intval[j], TRUE);
5737             }
5738             if (ptmpl)
5739               pname = weed_get_string_value(ptmpl, WEED_LEAF_NAME, NULL);
5740             else pname = lives_strdup("???");
5741             strval = lives_strdup_printf("%d", intval[j]);
5742             desc = lives_strdup_printf("(%s)", pname);
5743             lives_freep((void **)&pname);
5744           } else {
5745             if (etype == WEED_EVENT_TYPE_FILTER_INIT && (!strcmp(propnames[i], WEED_LEAF_IN_TRACKS)
5746                 || !strcmp(propnames[i], WEED_LEAF_OUT_TRACKS))) {
5747               if (mainw->multitrack) {
5748                 iname = weed_get_string_value(event, WEED_LEAF_FILTER, NULL);
5749                 if (iname) {
5750                   ie_idx = weed_get_idx_for_hashname(iname, TRUE);
5751                 }
5752                 lives_freep((void **)&iname);
5753                 strval = lives_strdup_printf("%d", intval[j]);
5754                 desc = lives_strdup_printf("(%s)",
5755                                            (tmp = get_track_name(mainw->multitrack, intval[j],
5756                                                   is_pure_audio(get_weed_filter(ie_idx), FALSE))));
5757                 lives_free(tmp);
5758               } else {
5759                 strval = lives_strdup_printf("%d", intval[j]);
5760                 desc = lives_strdup_printf("(%s)", intval[j] == 0 ? _("foreground clip")
5761                                            : _("background_clip"));
5762               }
5763             } else {
5764               if (0);
5765               /* if (etype == WEED_EVENT_TYPE_FILTER_INIT && (!strcmp(propnames[i], WEED_LEAF_IN_COUNT) */
5766               /* 						  || !strcmp(propnames[i], WEED_LEAF_OUT_COUNT))) { */
5767               /* 	iname = weed_get_string_value(event, WEED_LEAF_FILTER, NULL); */
5768               /* 	if (iname) { */
5769               /* 	  ie_idx = weed_get_idx_for_hashname(iname, TRUE); */
5770               /* 	} */
5771               /* 	strval = lives_strdup_printf("%d		(%s X %d)", intval[j], weed_chantmpl_get_name(...etc)); */
5772               /* 	lives_freep((void **)&iname); */
5773               /* } */
5774               else {
5775                 switch (seed_type) {
5776                 // format each element of value
5777                 case WEED_SEED_INT:
5778                   strval = lives_strdup_printf("%d", intval[j]);
5779                   break;
5780                 case WEED_SEED_INT64:
5781                   strval = lives_strdup_printf("%"PRId64, int64val[j]);
5782                   break;
5783                 case WEED_SEED_DOUBLE:
5784                   strval = lives_strdup_printf("%.4f", doubval[j]);
5785                   break;
5786                 case WEED_SEED_BOOLEAN:
5787                   if (intval[j] == WEED_TRUE) strval = (_("TRUE"));
5788                   else strval = (_("FALSE"));
5789                   break;
5790                 case WEED_SEED_STRING:
5791                   if (etype == WEED_EVENT_TYPE_FILTER_INIT && (!strcmp(propnames[i], WEED_LEAF_FILTER))) {
5792                     ie_idx = weed_get_idx_for_hashname(string[j], TRUE);
5793                     strval = weed_filter_idx_get_name(ie_idx, FALSE, FALSE);
5794                   } else strval = lives_strdup(string[j]);
5795                   lives_free(string[j]);
5796                   break;
5797                 case WEED_SEED_VOIDPTR:
5798                   if (etype == WEED_EVENT_TYPE_FILTER_DEINIT || etype == WEED_EVENT_TYPE_FILTER_MAP
5799                       || etype == WEED_EVENT_TYPE_PARAM_CHANGE) {
5800                     if (!(lives_strncmp(propnames[i], WEED_LEAF_INIT_EVENT, inistrlen))) {
5801                       ievent = (weed_plant_t *)voidval[j];
5802                       if (ievent) {
5803                         lives_freep((void **)&iname);
5804                         iname = weed_get_string_value(ievent, WEED_LEAF_FILTER, NULL);
5805                         if (iname) {
5806                           ie_idx = weed_get_idx_for_hashname(iname, TRUE);
5807                           fname = weed_filter_idx_get_name(ie_idx, FALSE, FALSE);
5808                           strval = lives_strdup_printf("%p", voidval[j]);
5809                           desc = lives_strdup_printf("(%s)", fname);
5810                           lives_freep((void **)&fname);
5811                         }
5812                         lives_freep((void **)&iname);
5813                       }
5814                     }
5815                   }
5816                   if (!strval) {
5817                     if (voidval[j]) strval = lives_strdup_printf("%p", voidval[j]);
5818                     else strval = lives_strdup(" - ");
5819                   }
5820                   break;
5821                 case WEED_SEED_PLANTPTR:
5822                   strval = lives_strdup_printf("%p", voidval[j]);
5823                   break;
5824                 default:
5825                   strval = lives_strdup("???");
5826                   break;
5827 		  // *INDENT-OFF*
5828                 }}}}
5829 	  // *INDENT-ON*
5830 
5831           // attach to treestore
5832           if (j == 0) {
5833             if (num_elems == 1) {
5834               lives_tree_store_set(treestore, &iter2, KEY_COLUMN, propnames[i], VALUE_COLUMN, strval, -1);
5835               lives_tree_store_set(treestore, &iter2, DESC_COLUMN, desc, -1);
5836             } else {
5837               lives_tree_store_set(treestore, &iter2, KEY_COLUMN, propnames[i], VALUE_COLUMN, "", -1);
5838               lives_tree_store_append(treestore, &iter3, &iter2);
5839               lives_tree_store_set(treestore, &iter3, VALUE_COLUMN, strval, -1);
5840               lives_tree_store_set(treestore, &iter3, DESC_COLUMN, desc, -1);
5841             }
5842           } else {
5843             lives_tree_store_append(treestore, &iter3, &iter2);
5844             lives_tree_store_set(treestore, &iter3, VALUE_COLUMN, strval, -1);
5845           }
5846           lives_freep((void **)&desc);
5847           lives_freep((void **)&strval);
5848         }
5849 
5850         switch (seed_type) {
5851         // free temp memory
5852         case WEED_SEED_INT:
5853         case WEED_SEED_BOOLEAN:
5854           lives_free(intval);
5855           break;
5856         case WEED_SEED_INT64:
5857           lives_free(int64val);
5858           break;
5859         case WEED_SEED_DOUBLE:
5860           lives_free(doubval);
5861           break;
5862         case WEED_SEED_STRING:
5863           lives_free(string);
5864           break;
5865         case WEED_SEED_VOIDPTR:
5866         case WEED_SEED_PLANTPTR:
5867           lives_free(voidval);
5868           break;
5869         default: break;
5870         }
5871         lives_free(propnames[i]);
5872       }
5873 
5874       lives_free(propnames);
5875 
5876       // now add the new treeview
5877 
5878       lives_free(final);
5879 
5880       // timecode
5881       tc_secs = tc / TICKS_PER_SECOND;
5882       tc -= tc_secs * TICKS_PER_SECOND;
5883       text = lives_strdup_printf(_("Timecode=%"PRId64".%"PRId64), tc_secs, tc);
5884       label = lives_standard_label_new(text);
5885       lives_free(text);
5886       lives_widget_set_valign(label, LIVES_ALIGN_START);
5887 
5888       lives_table_attach(LIVES_TABLE(table), label, 0, 1, currow, currow + 1,
5889                          (LiVESAttachOptions)(LIVES_EXPAND), (LiVESAttachOptions)(0), 0, 0);
5890 
5891       if (WEED_PLANT_IS_EVENT_LIST(event))
5892         ltext = "Event list";
5893       else {
5894         // event type
5895         switch (etype) {
5896         case WEED_EVENT_TYPE_FRAME:
5897           if (WEED_EVENT_IS_AUDIO_FRAME(event))
5898             ltext = "Frame with audio";
5899           else
5900             ltext = "Frame";
5901           break;
5902         case WEED_EVENT_TYPE_FILTER_INIT:
5903           ltext = "Filter on"; break;
5904         case WEED_EVENT_TYPE_FILTER_DEINIT:
5905           ltext = "Filter off"; break;
5906         case WEED_EVENT_TYPE_PARAM_CHANGE:
5907           ltext = "Parameter change"; break;
5908         case WEED_EVENT_TYPE_FILTER_MAP:
5909           ltext = "Filter map"; break;
5910         case WEED_EVENT_TYPE_MARKER:
5911           ltext = "Marker"; break;
5912         default:
5913           ltext = lives_strdup_printf("unknown event type %d", etype);
5914           label = lives_standard_label_new(ltext);
5915           ltext = NULL;
5916         }
5917       }
5918       if (ltext) {
5919         text = lives_strdup_printf("<big><b>%s</b></big>", ltext);
5920         widget_opts.use_markup = TRUE;
5921         label = lives_standard_label_new(text);
5922         widget_opts.use_markup = FALSE;
5923         lives_free(text);
5924         lives_widget_set_valign(label, LIVES_ALIGN_START);
5925       }
5926 
5927       lives_table_attach(LIVES_TABLE(table), label, 1, 2, currow, currow + 1,
5928                          (LiVESAttachOptions)(LIVES_EXPAND), (LiVESAttachOptions)(0), 0, 0);
5929 
5930       // event id
5931       text = lives_strdup_printf(_("Event id=%p"), (void *)event);
5932       label = lives_standard_label_new(text);
5933       lives_free(text);
5934       lives_widget_set_valign(label, LIVES_ALIGN_START);
5935 
5936       lives_table_attach(LIVES_TABLE(table), label, 2, 3, currow, currow + 1,
5937                          (LiVESAttachOptions)(LIVES_EXPAND),
5938                          (LiVESAttachOptions)(0), 0, 0);
5939 
5940       // properties
5941       tree = lives_tree_view_new_with_model(LIVES_TREE_MODEL(treestore));
5942 
5943       if (palette->style & STYLE_1) {
5944         lives_widget_set_base_color(tree, LIVES_WIDGET_STATE_NORMAL, &palette->info_base);
5945         lives_widget_set_text_color(tree, LIVES_WIDGET_STATE_NORMAL, &palette->info_text);
5946       }
5947 
5948       renderer = lives_cell_renderer_text_new();
5949       column = lives_tree_view_column_new_with_attributes(NULL,
5950                renderer, LIVES_TREE_VIEW_COLUMN_TEXT, TITLE_COLUMN, NULL);
5951 
5952       gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5953       lives_tree_view_append_column(LIVES_TREE_VIEW(tree), column);
5954 
5955       renderer = lives_cell_renderer_text_new();
5956       GValue gval = G_VALUE_INIT;
5957       g_value_init(&gval, G_TYPE_INT);
5958       g_value_set_int(&gval, 12);
5959       gtk_cell_renderer_set_padding(renderer, widget_opts.packing_width, 0);
5960       column = lives_tree_view_column_new_with_attributes(_("Keys"),
5961                renderer, LIVES_TREE_VIEW_COLUMN_TEXT, KEY_COLUMN, NULL);
5962       gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5963       gtk_tree_view_column_set_expand(column, TRUE);
5964       lives_tree_view_append_column(LIVES_TREE_VIEW(tree), column);
5965 
5966       renderer = lives_cell_renderer_text_new();
5967       gtk_cell_renderer_set_padding(renderer, widget_opts.packing_width, 0);
5968       column = lives_tree_view_column_new_with_attributes(_("Values"),
5969                renderer, LIVES_TREE_VIEW_COLUMN_TEXT, VALUE_COLUMN, NULL);
5970       gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5971       gtk_tree_view_column_set_expand(column, TRUE);
5972       lives_tree_view_append_column(LIVES_TREE_VIEW(tree), column);
5973 
5974       renderer = lives_cell_renderer_text_new();
5975       gtk_cell_renderer_set_padding(renderer, widget_opts.packing_width, 0);
5976       column = lives_tree_view_column_new_with_attributes(_("Description"),
5977                renderer, LIVES_TREE_VIEW_COLUMN_TEXT, DESC_COLUMN, NULL);
5978       gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5979       gtk_tree_view_column_set_expand(column, TRUE);
5980       lives_tree_view_append_column(LIVES_TREE_VIEW(tree), column);
5981 
5982       lives_table_attach(LIVES_TABLE(table), tree, 3, 6, currow, currow + 1,
5983                          (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
5984                          (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND), 0, 0);
5985 
5986 #if GTK_CHECK_VERSION(3, 0, 0)
5987       lives_signal_sync_connect(LIVES_GUI_OBJECT(tree), LIVES_WIDGET_ROW_EXPANDED_SIGNAL,
5988                                 LIVES_GUI_CALLBACK(rowexpand), NULL);
5989 
5990       lives_widget_set_size_request(tree, -1, TREE_ROW_HEIGHT);
5991 #endif
5992       currow++;
5993       gtk_tree_view_set_fixed_height_mode(LIVES_TREE_VIEW(tree), TRUE);
5994     }
5995     if (event == event_list) event = get_first_event(event_list);
5996     else event = get_next_event(event);
5997   }
5998 
5999   lives_freep((void **)&iname);
6000 
6001   widget_opts.apply_theme = 0;
6002   scrolledwindow = lives_standard_scrolled_window_new(winsize_h, winsize_v, table);
6003   widget_opts.apply_theme = woat;
6004 
6005 #if !GTK_CHECK_VERSION(3, 0, 0)
6006   if (palette->style & STYLE_1) {
6007     lives_widget_set_bg_color(top_vbox, LIVES_WIDGET_STATE_NORMAL, &palette->info_base);
6008     lives_widget_set_fg_color(top_vbox, LIVES_WIDGET_STATE_NORMAL, &palette->info_text);
6009     lives_widget_set_bg_color(lives_bin_get_child(LIVES_BIN(scrolledwindow)), LIVES_WIDGET_STATE_NORMAL, &palette->info_base);
6010   }
6011 #endif
6012 
6013   lives_box_pack_start(LIVES_BOX(top_vbox), scrolledwindow, TRUE, TRUE, widget_opts.packing_height);
6014 
6015   if (prefs->show_dev_opts) {
6016     if (CURRENT_CLIP_IS_VALID) {
6017       char *tmp = lives_strdup_printf("Quantise to %.4f fps", cfile->fps);
6018       LiVESWidget *qbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(event_dialog), NULL, tmp,
6019                              LIVES_RESPONSE_OK);
6020       lives_free(tmp);
6021       lives_signal_sync_connect(LIVES_GUI_OBJECT(qbutton), LIVES_WIDGET_CLICKED_SIGNAL,
6022                                 LIVES_GUI_CALLBACK(quant_clicked), (livespointer)event_list);
6023     }
6024   }
6025   ok_button = lives_dialog_add_button_from_stock(LIVES_DIALOG(event_dialog), LIVES_STOCK_CLOSE, _("_Close Window"),
6026               LIVES_RESPONSE_OK);
6027 
6028   lives_button_grab_default_special(ok_button);
6029 
6030   lives_widget_add_accelerator(ok_button, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
6031                                LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
6032 
6033   lives_signal_sync_connect(LIVES_GUI_OBJECT(ok_button), LIVES_WIDGET_CLICKED_SIGNAL,
6034                             LIVES_GUI_CALLBACK(lives_general_button_clicked), NULL);
6035 
6036   if (prefs->gui_monitor != 0) {
6037     lives_window_center(LIVES_WINDOW(event_dialog));
6038   }
6039 
6040   daa = lives_dialog_get_action_area(LIVES_DIALOG(event_dialog));
6041   lives_button_box_set_layout(LIVES_BUTTON_BOX(daa), LIVES_BUTTONBOX_SPREAD);
6042 
6043   if (prefs->open_maximised) {
6044     lives_window_unmaximize(LIVES_WINDOW(event_dialog));
6045     lives_window_maximize(LIVES_WINDOW(event_dialog));
6046   }
6047 
6048   lives_widget_show_all(event_dialog);
6049 
6050   return event_dialog;
6051 }
6052 
6053 
rdetw_spinh_changed(LiVESSpinButton * spinbutton,livespointer user_data)6054 void rdetw_spinh_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
6055   render_details *rdet = (render_details *)user_data;
6056   rdet->height = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
6057 }
6058 
6059 
rdetw_spinw_changed(LiVESSpinButton * spinbutton,livespointer user_data)6060 void rdetw_spinw_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
6061   render_details *rdet = (render_details *)user_data;
6062   rdet->width = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(spinbutton));
6063 }
6064 
6065 
rdetw_spinf_changed(LiVESSpinButton * spinbutton,livespointer user_data)6066 void rdetw_spinf_changed(LiVESSpinButton * spinbutton, livespointer user_data) {
6067   render_details *rdet = (render_details *)user_data;
6068   rdet->fps = lives_spin_button_get_value(LIVES_SPIN_BUTTON(spinbutton));
6069 }
6070 
6071 
add_video_options(LiVESWidget ** spwidth,int defwidth,LiVESWidget ** spheight,int defheight,LiVESWidget ** spfps,double deffps,LiVESWidget ** spframes,int defframes,boolean add_aspect,LiVESWidget * extra)6072 LiVESWidget *add_video_options(LiVESWidget **spwidth, int defwidth, LiVESWidget **spheight, int defheight,
6073                                LiVESWidget **spfps, double deffps, LiVESWidget **spframes, int defframes,
6074                                boolean add_aspect, LiVESWidget * extra) {
6075   // add video options to multitrack enter, etc
6076   LiVESWidget *vbox, *hbox, *layout;
6077   LiVESWidget *frame = lives_standard_frame_new(_("Video"), 0., FALSE);
6078 
6079   double width_step = 4.;
6080   double height_step = 4.;
6081 
6082   vbox = lives_vbox_new(FALSE, 0);
6083   lives_container_add(LIVES_CONTAINER(frame), vbox);
6084 
6085   layout = lives_layout_new(LIVES_BOX(vbox));
6086 
6087   hbox = lives_layout_hbox_new(LIVES_LAYOUT(layout));
6088   *spwidth = lives_standard_spin_button_new
6089              (_("_Width"), defwidth, width_step, MAX_FRAME_WIDTH, width_step, width_step, 0, LIVES_BOX(hbox), NULL);
6090   lives_spin_button_set_snap_to_multiples(LIVES_SPIN_BUTTON(*spwidth), width_step);
6091   lives_spin_button_update(LIVES_SPIN_BUTTON(*spwidth));
6092 
6093   hbox = lives_layout_hbox_new(LIVES_LAYOUT(layout));
6094   *spheight = lives_standard_spin_button_new
6095               (_("_Height"), defheight, height_step, MAX_FRAME_WIDTH, height_step, height_step, 0, LIVES_BOX(hbox), NULL);
6096   lives_spin_button_set_snap_to_multiples(LIVES_SPIN_BUTTON(*spheight), height_step);
6097   lives_spin_button_update(LIVES_SPIN_BUTTON(*spheight));
6098 
6099   // add aspect button ?
6100   if (add_aspect && CURRENT_CLIP_IS_VALID) {
6101     // add "aspectratio" widget
6102     hbox = lives_layout_hbox_new(LIVES_LAYOUT(layout));
6103     add_aspect_ratio_button(LIVES_SPIN_BUTTON(*spwidth), LIVES_SPIN_BUTTON(*spheight), LIVES_BOX(hbox));
6104   }
6105 
6106   hbox = lives_layout_row_new(LIVES_LAYOUT(layout));
6107 
6108   if (spframes) {
6109     *spframes = lives_standard_spin_button_new
6110                 (_("_Number of frames"), defframes, 1., 100000, 1., 5., 0, LIVES_BOX(hbox), NULL);
6111     hbox = lives_layout_hbox_new(LIVES_LAYOUT(layout));
6112   }
6113 
6114   *spfps = lives_standard_spin_button_new
6115            (_("_Frames per second"), deffps, 1., FPS_MAX, 1., 10., 0, LIVES_BOX(hbox), NULL);
6116 
6117   if (extra) lives_box_pack_start(LIVES_BOX(vbox), extra, FALSE, FALSE, widget_opts.packing_height);
6118 
6119   return frame;
6120 }
6121 
6122 
add_fade_elements(render_details * rdet,LiVESWidget * hbox,boolean is_video)6123 static void add_fade_elements(render_details * rdet, LiVESWidget * hbox, boolean is_video) {
6124   LiVESWidget *cb;
6125   LiVESWidget *vbox = NULL;
6126   if (is_video) {
6127     vbox = lives_vbox_new(FALSE, widget_opts.packing_height >> 1);
6128     lives_box_pack_start(LIVES_BOX(hbox), vbox, FALSE, TRUE, 0);
6129     hbox = lives_hbox_new(FALSE, 0);
6130     lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, TRUE, 0);
6131   }
6132   add_fill_to_box(LIVES_BOX(hbox));
6133 
6134   cb = lives_standard_check_button_new(_("Fade in over"), FALSE, LIVES_BOX(hbox), NULL);
6135 
6136   widget_opts.swap_label = TRUE;
6137   if (!is_video) {
6138     rdet->afade_in = lives_standard_spin_button_new(_("seconds"),
6139                      10., 0., 1000., 1., 1., 2,
6140                      LIVES_BOX(hbox), NULL);
6141     toggle_sets_sensitive(LIVES_TOGGLE_BUTTON(cb), rdet->afade_in, FALSE);
6142   } else {
6143     rdet->vfade_in = lives_standard_spin_button_new(_("seconds"),
6144                      10., 0., 1000., 1., 1., 2,
6145                      LIVES_BOX(hbox), NULL);
6146     toggle_sets_sensitive(LIVES_TOGGLE_BUTTON(cb), rdet->vfade_in, FALSE);
6147   }
6148   widget_opts.swap_label = FALSE;
6149 
6150   add_fill_to_box(LIVES_BOX(hbox));
6151 
6152   cb = lives_standard_check_button_new(_("Fade out over"), FALSE, LIVES_BOX(hbox), NULL);
6153 
6154   widget_opts.swap_label = TRUE;
6155   if (!is_video) {
6156     rdet->afade_out = lives_standard_spin_button_new(_("seconds"),
6157                       10., 0., 1000., 1., 1., 2,
6158                       LIVES_BOX(hbox), NULL);
6159     toggle_sets_sensitive(LIVES_TOGGLE_BUTTON(cb), rdet->afade_out, FALSE);
6160   } else {
6161     rdet->vfade_out = lives_standard_spin_button_new(_("seconds"),
6162                       10., 0., 1000., 1., 1., 2,
6163                       LIVES_BOX(hbox), NULL);
6164     toggle_sets_sensitive(LIVES_TOGGLE_BUTTON(cb), rdet->vfade_out, FALSE);
6165   }
6166   widget_opts.swap_label = FALSE;
6167 
6168   add_fill_to_box(LIVES_BOX(hbox));
6169 
6170   if (is_video) {
6171     lives_colRGBA64_t rgba;
6172     LiVESWidget *sp_red, *sp_green, *sp_blue;
6173     rgba.red = rgba.green = rgba.blue = 0;
6174     hbox = lives_hbox_new(FALSE, 0);
6175     lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, TRUE, 0);
6176     add_fill_to_box(LIVES_BOX(hbox));
6177     rdet->vfade_col = lives_standard_color_button_new(LIVES_BOX(hbox), _("Fade Color"),
6178                       FALSE, &rgba, &sp_red,
6179                       &sp_green, &sp_blue, NULL);
6180   }
6181 }
6182 
6183 
add_audio_options(LiVESWidget ** cbbackaudio,LiVESWidget ** cbpertrack)6184 LiVESWidget *add_audio_options(LiVESWidget **cbbackaudio, LiVESWidget **cbpertrack) {
6185   LiVESWidget *hbox = lives_hbox_new(FALSE, 0);
6186 
6187   *cbbackaudio = lives_standard_check_button_new(_("Enable _backing audio track"), FALSE, LIVES_BOX(hbox), NULL);
6188 
6189   add_fill_to_box(LIVES_BOX(hbox));
6190 
6191   *cbpertrack = lives_standard_check_button_new(_("Audio track _per video track"), FALSE, LIVES_BOX(hbox), NULL);
6192 
6193   return hbox;
6194 }
6195 
6196 
rdet_use_current(LiVESButton * button,livespointer user_data)6197 static void rdet_use_current(LiVESButton * button, livespointer user_data) {
6198   render_details *rdet = (render_details *)user_data;
6199   const lives_special_aspect_t *aspect = NULL;
6200   char *arate, *achans, *asamps;
6201   int aendian;
6202 
6203   if (!CURRENT_CLIP_IS_VALID) return;
6204 
6205   if (CURRENT_CLIP_HAS_VIDEO) {
6206     lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_width), (double)cfile->hsize);
6207     lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_height), (double)cfile->vsize);
6208     lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_fps), cfile->fps);
6209     lives_spin_button_update(LIVES_SPIN_BUTTON(rdet->spinbutton_width));
6210 
6211     aspect = paramspecial_get_aspect();
6212 
6213     if (aspect && aspect->lockbutton) lives_widget_show_all(aspect->lockbutton);
6214 
6215     rdet->ratio_fps = cfile->ratio_fps;
6216   }
6217 
6218   if (cfile->achans > 0) {
6219     lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton), TRUE);
6220 
6221     arate = lives_strdup_printf("%d", cfile->arate);
6222     lives_entry_set_text(LIVES_ENTRY(resaudw->entry_arate), arate);
6223     lives_free(arate);
6224 
6225     achans = lives_strdup_printf("%d", cfile->achans);
6226     lives_entry_set_text(LIVES_ENTRY(resaudw->entry_achans), achans);
6227     lives_free(achans);
6228 
6229     asamps = lives_strdup_printf("%d", cfile->asampsize);
6230     lives_entry_set_text(LIVES_ENTRY(resaudw->entry_asamps), asamps);
6231     lives_free(asamps);
6232 
6233     aendian = cfile->signed_endian;
6234 
6235     if (aendian & AFORM_UNSIGNED) {
6236       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned), TRUE);
6237     } else {
6238       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_signed), TRUE);
6239     }
6240 
6241     if (aendian & AFORM_BIG_ENDIAN) {
6242       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend), TRUE);
6243     } else {
6244       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_littleend), TRUE);
6245     }
6246   } else {
6247     lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton), FALSE);
6248   }
6249 }
6250 
6251 
create_render_details(int type)6252 render_details *create_render_details(int type) {
6253   // type == 1 :: pre-save (specified)
6254   // type == 2 :: render to new clip (!specified)
6255   // type == 3 :: enter multitrack (!specified)
6256   // type == 4 :: change during multitrack (!specified)
6257   // type == 5 :: transcode clip (!specified) -> becomes type 2
6258 
6259   LiVESWidget *label;
6260   LiVESWidget *top_vbox;
6261   LiVESWidget *dialog_vbox;
6262   LiVESWidget *scrollw = NULL;
6263   LiVESWidget *hbox;
6264   LiVESWidget *vbox;
6265   LiVESWidget *frame;
6266   LiVESWidget *cancelbutton;
6267   LiVESWidget *alabel;
6268   LiVESWidget *daa;
6269   LiVESWidget *cb_letter;
6270   LiVESWidget *spillover;
6271 
6272   LiVESAccelGroup *rdet_accel_group;
6273 
6274   LiVESList *ofmt_all = NULL;
6275   LiVESList *ofmt = NULL;
6276   LiVESList *encoders = NULL;
6277 
6278   char **array;
6279 
6280   char *tmp, *tmp2, *tmp3;
6281   char *title;
6282 
6283   boolean needs_new_encoder = FALSE;
6284   boolean no_opts = FALSE;
6285 
6286   int width, height, dwidth, dheight, spht, maxwidth, maxheight;
6287 
6288   int scrw = GUI_SCREEN_WIDTH;
6289   int scrh = GUI_SCREEN_HEIGHT;
6290   int dbw;
6291 
6292   int i;
6293 
6294   if (type == 5) {
6295     no_opts = TRUE;
6296     type = 2;
6297   }
6298   mainw->no_context_update = TRUE;
6299 
6300   rdet = (render_details *)lives_calloc(1, sizeof(render_details));
6301 
6302   rdet->is_encoding = FALSE;
6303 
6304   if ((type != 1 && type != 4) || !IS_VALID_CLIP(mainw->current_file) || mainw->current_file == mainw->scrap_file) {
6305     rdet->width = prefs->mt_def_width;
6306     rdet->height = prefs->mt_def_height;
6307     rdet->fps = prefs->mt_def_fps;
6308     rdet->ratio_fps = FALSE;
6309 
6310     rdet->arate = prefs->mt_def_arate;
6311     rdet->achans = prefs->mt_def_achans;
6312     rdet->asamps = prefs->mt_def_asamps;
6313     rdet->aendian = prefs->mt_def_signed_endian;
6314   } else {
6315     rdet->width = cfile->hsize;
6316     rdet->height = cfile->vsize;
6317     rdet->fps = cfile->fps;
6318     rdet->ratio_fps = cfile->ratio_fps;
6319 
6320     rdet->arate = cfile->arate;
6321     rdet->achans = cfile->achans;
6322     rdet->asamps = cfile->asampsize;
6323     rdet->aendian = cfile->signed_endian;
6324   }
6325 
6326   rdet->enc_changed = FALSE;
6327 
6328   if (type == 3 || type == 4) {
6329     title = (_("Multitrack Details"));
6330   } else if (type == 1) title = (_("Encoding Details"));
6331   else title = (_("New Clip Details"));
6332 
6333   maxwidth = width = scrw - SCR_WIDTH_SAFETY;
6334   maxheight = height = scrh - SCR_HEIGHT_SAFETY;
6335 
6336   if (type == 1) {
6337     width /= 2;
6338     height /= 2;
6339   }
6340 
6341   widget_opts.expand = LIVES_EXPAND_NONE;
6342   rdet->dialog = lives_standard_dialog_new(title, FALSE, width, height);
6343   widget_opts.expand = LIVES_EXPAND_DEFAULT;
6344 
6345   lives_free(title);
6346 
6347   rdet_accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
6348   lives_window_add_accel_group(LIVES_WINDOW(rdet->dialog), rdet_accel_group);
6349 
6350   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(rdet->dialog));
6351 
6352   top_vbox = lives_vbox_new(FALSE, 0);
6353 
6354   if (type != 1) {
6355     dbw = widget_opts.border_width;
6356     widget_opts.border_width = 0;
6357     // need to set a large enough default here
6358     scrollw = lives_standard_scrolled_window_new(width * .8, height * .5, top_vbox);
6359     widget_opts.border_width = dbw;
6360     lives_box_pack_start(LIVES_BOX(dialog_vbox), scrollw, FALSE, TRUE, 0);
6361   } else lives_box_pack_start(LIVES_BOX(dialog_vbox), top_vbox, FALSE, TRUE, 0);
6362 
6363   lives_container_set_border_width(LIVES_CONTAINER(top_vbox), 0);
6364 
6365   daa = lives_dialog_get_action_area(LIVES_DIALOG(rdet->dialog));
6366 
6367   rdet->always_checkbutton = lives_standard_check_button_new((tmp = (_("_Always use these values"))), FALSE,
6368                              LIVES_BOX(daa),
6369                              (tmp2 = lives_strdup(
6370                                        H_("Check this button to always use these values when entering "
6371                                           "multitrack mode. "
6372                                           "Choice can be re-enabled from Preferences / Multitrack"))));
6373   lives_button_box_make_first(LIVES_BUTTON_BOX(daa), widget_opts.last_container);
6374 
6375   rdet->always_hbox = widget_opts.last_container;
6376   if (type == 1 || type == 2) gtk_widget_set_no_show_all(rdet->always_hbox, TRUE);
6377 
6378   lives_free(tmp); lives_free(tmp2);
6379 
6380   if (type == 4) {
6381     hbox = lives_hbox_new(FALSE, 0);
6382     cb_letter = lives_standard_check_button_new(_("Apply letterboxing"), prefs->letterbox_mt,
6383                 LIVES_BOX(hbox), (tmp = H_("Defines whether black borders will be added when resizing frames\n"
6384                                            "in order to preserve the original aspect ratio")));
6385     lives_free(tmp);
6386     lives_signal_sync_connect(LIVES_GUI_OBJECT(cb_letter), LIVES_WIDGET_TOGGLED_SIGNAL,
6387                               LIVES_GUI_CALLBACK(toggle_sets_pref), (livespointer)PREF_LETTERBOXMT);
6388   } else hbox = NULL;
6389 
6390   frame = add_video_options(&rdet->spinbutton_width, rdet->width, &rdet->spinbutton_height, rdet->height, &rdet->spinbutton_fps,
6391                             rdet->fps, NULL, 0., TRUE, hbox);
6392   lives_box_pack_start(LIVES_BOX(top_vbox), frame, FALSE, TRUE, 0);
6393 
6394   if (type == 4) {
6395     const lives_special_aspect_t *aspect = paramspecial_get_aspect();
6396     if (aspect && aspect->lockbutton) {
6397       if (lives_lock_button_get_locked(LIVES_BUTTON(aspect->lockbutton)))
6398         lives_lock_button_toggle(LIVES_BUTTON(aspect->lockbutton));
6399     }
6400   }
6401 
6402   if (type == 1) gtk_widget_set_no_show_all(frame, TRUE);
6403 
6404   if (type == 2) {
6405     if (mainw->event_list && weed_plant_has_leaf(mainw->event_list, WEED_LEAF_FPS)) {
6406       lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_fps),
6407                                   weed_get_double_value(mainw->event_list, WEED_LEAF_FPS, NULL));
6408       lives_widget_set_sensitive(rdet->spinbutton_fps, FALSE);
6409     }
6410 
6411     if (!no_opts) {
6412       // add clip name entry
6413       rdet->clipname_entry = lives_standard_entry_new((tmp = (_("New clip name"))),
6414                              (tmp2 = get_untitled_name(mainw->untitled_number)),
6415                              MEDIUM_ENTRY_WIDTH, 256, LIVES_BOX(top_vbox),
6416                              (tmp3 = (_("The name to give the clip in the Clips menu"))));
6417       lives_free(tmp); lives_free(tmp2); lives_free(tmp3);
6418     }
6419 
6420     /* add_fill_to_box(LIVES_BOX(lives_bin_get_child(LIVES_BIN(frame)))); */
6421     /* hbox = lives_hbox_new(FALSE, 0); */
6422     /* lives_container_add(LIVES_CONTAINER(lives_bin_get_child(LIVES_BIN(frame))), hbox); */
6423     /* add_fill_to_box(LIVES_BOX(hbox)); */
6424     /* add_fade_elements(rdet, hbox, TRUE); */
6425   }
6426   // call these here since adding the widgets may have altered their values
6427   rdetw_spinw_changed(LIVES_SPIN_BUTTON(rdet->spinbutton_width), (livespointer)rdet);
6428   rdetw_spinh_changed(LIVES_SPIN_BUTTON(rdet->spinbutton_height), (livespointer)rdet);
6429 
6430   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rdet->spinbutton_width), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
6431                                   LIVES_GUI_CALLBACK(rdetw_spinw_changed), rdet);
6432 
6433   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rdet->spinbutton_height), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
6434                                   LIVES_GUI_CALLBACK(rdetw_spinh_changed), rdet);
6435 
6436   if (type == 4 && mainw->multitrack->event_list) lives_widget_set_sensitive(rdet->spinbutton_fps, FALSE);
6437 
6438   lives_signal_sync_connect_after(LIVES_GUI_OBJECT(rdet->spinbutton_fps), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
6439                                   LIVES_GUI_CALLBACK(rdetw_spinf_changed), rdet);
6440 
6441   rdet->pertrack_checkbutton = lives_check_button_new();
6442   rdet->backaudio_checkbutton = lives_check_button_new();
6443 
6444   ////// add audio part
6445   resaudw = NULL;
6446   if (type == 3 || type == 2) resaudw = create_resaudw(3, rdet, top_vbox); // enter mt, render to clip
6447   else if (type == 4 && cfile->achans != 0) {
6448     resaudw = create_resaudw(10, rdet, top_vbox); // change during mt. Channels fixed.
6449   }
6450 
6451   if (type == 2) {
6452     add_fill_to_box(LIVES_BOX(resaudw->vbox));
6453 
6454     hbox = lives_hbox_new(FALSE, 0);
6455     lives_box_pack_start(LIVES_BOX(resaudw->vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
6456     rdet->norm_after = lives_standard_check_button_new(_("_Normalise audio after rendering"),
6457                        TRUE, LIVES_BOX(hbox), NULL);
6458     toggle_sets_sensitive(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton), rdet->norm_after, FALSE);
6459 
6460     hbox = lives_hbox_new(FALSE, 0);
6461     lives_box_pack_start(LIVES_BOX(resaudw->vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
6462     add_fade_elements(rdet, hbox, FALSE);
6463   }
6464 
6465   if (type == 3) {
6466     // extra opts
6467     label = lives_standard_label_new(_("Options"));
6468     lives_box_pack_start(LIVES_BOX(top_vbox), label, FALSE, FALSE, widget_opts.packing_height);
6469     hbox = add_audio_options(&rdet->backaudio_checkbutton, &rdet->pertrack_checkbutton);
6470     lives_box_pack_start(LIVES_BOX(top_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
6471     lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(rdet->backaudio_checkbutton), prefs->mt_backaudio > 0);
6472 
6473     lives_widget_set_sensitive(rdet->backaudio_checkbutton,
6474                                lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton)));
6475 
6476     lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(rdet->pertrack_checkbutton), prefs->mt_pertrack_audio);
6477 
6478     lives_widget_set_sensitive(rdet->pertrack_checkbutton,
6479                                lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton)));
6480   }
6481 
6482   if (!no_opts) {
6483 #ifndef IS_MINGW
6484     if (capable->has_encoder_plugins) encoders = get_plugin_list(PLUGIN_ENCODERS, FALSE, NULL, NULL);
6485 #else
6486     if (capable->has_encoder_plugins) encoders = get_plugin_list(PLUGIN_ENCODERS, TRUE, NULL, NULL);
6487 #endif
6488 
6489     if (type != 1) encoders = filter_encoders_by_img_ext(encoders, prefs->image_ext);
6490     else {
6491       LiVESList *encs = encoders = filter_encoders_by_img_ext(encoders, get_image_ext_for_type(cfile->img_type));
6492       needs_new_encoder = TRUE;
6493       while (encs) {
6494         if (!strcmp((char *)encs->data, prefs->encoder.name)) {
6495           needs_new_encoder = FALSE;
6496           break;
6497         }
6498         encs = encs->next;
6499       }
6500     }
6501 
6502     if (type != 1) {
6503       add_hsep_to_box(LIVES_BOX(top_vbox));
6504       if (type != 3) {
6505         label = lives_standard_label_new(_("Options"));
6506         lives_box_pack_start(LIVES_BOX(top_vbox), label, FALSE, FALSE, widget_opts.packing_height);
6507       }
6508     }
6509 
6510     /////////////////////// add expander ////////////////////////////
6511 
6512     if (type != 1) {
6513       hbox = lives_hbox_new(FALSE, 0);
6514       lives_box_pack_start(LIVES_BOX(top_vbox), hbox, FALSE, FALSE, 0);
6515 
6516       vbox = lives_vbox_new(FALSE, 0);
6517 
6518       widget_opts.justify = LIVES_JUSTIFY_CENTER;
6519       lives_standard_expander_new(_("_Encoder preferences (optional)"), LIVES_BOX(hbox), vbox);
6520       widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
6521     } else vbox = top_vbox;
6522 
6523     add_fill_to_box(LIVES_BOX(vbox));
6524 
6525     widget_opts.justify = LIVES_JUSTIFY_CENTER;
6526     label = lives_standard_label_new(_("Target encoder"));
6527     widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
6528     lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height);
6529 
6530     if (type != 1) {
6531       rdet->encoder_name = lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_ANY]);
6532       encoders = lives_list_prepend(encoders, lives_strdup(rdet->encoder_name));
6533     } else {
6534       rdet->encoder_name = lives_strdup(prefs->encoder.name);
6535     }
6536 
6537     hbox = lives_hbox_new(FALSE, 0);
6538     add_spring_to_box(LIVES_BOX(hbox), 0);
6539     rdet->encoder_combo = lives_standard_combo_new(NULL, encoders, LIVES_BOX(hbox), NULL);
6540     lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
6541     lives_widget_set_halign(rdet->encoder_combo, LIVES_ALIGN_CENTER);
6542     add_spring_to_box(LIVES_BOX(hbox), 0);
6543 
6544     rdet->encoder_name_fn = lives_signal_sync_connect_after(LIVES_COMBO(rdet->encoder_combo), LIVES_WIDGET_CHANGED_SIGNAL,
6545                             LIVES_GUI_CALLBACK(on_encoder_entry_changed), rdet);
6546 
6547     lives_signal_handler_block(rdet->encoder_combo, rdet->encoder_name_fn);
6548     lives_combo_set_active_string(LIVES_COMBO(rdet->encoder_combo), rdet->encoder_name);
6549     lives_signal_handler_unblock(rdet->encoder_combo, rdet->encoder_name_fn);
6550 
6551     lives_list_free_all(&encoders);
6552 
6553     if (type != 1) {
6554       ofmt = lives_list_append(ofmt, lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_ANY]));
6555     } else {
6556       add_fill_to_box(LIVES_BOX(vbox));
6557       if (capable->has_encoder_plugins) {
6558         // reqest formats from the encoder plugin
6559         if ((ofmt_all = plugin_request_by_line(PLUGIN_ENCODERS, prefs->encoder.name, "get_formats")) != NULL) {
6560           for (i = 0; i < lives_list_length(ofmt_all); i++) {
6561             if (get_token_count((char *)lives_list_nth_data(ofmt_all, i), '|') > 2) {
6562               array = lives_strsplit((char *)lives_list_nth_data(ofmt_all, i), "|", -1);
6563               if (!strcmp(array[0], prefs->encoder.of_name)) {
6564                 prefs->encoder.of_allowed_acodecs = atoi(array[2]);
6565               }
6566               ofmt = lives_list_append(ofmt, lives_strdup(array[1]));
6567               lives_strfreev(array);
6568             }
6569           }
6570           lives_list_free_all(&ofmt_all);
6571         } else {
6572           future_prefs->encoder.of_allowed_acodecs = 0;
6573         }
6574       }
6575     }
6576 
6577     widget_opts.justify = LIVES_JUSTIFY_CENTER;
6578     label = lives_standard_label_new(_("Output format"));
6579     widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
6580     lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height);
6581 
6582     hbox = lives_hbox_new(FALSE, 0);
6583     add_spring_to_box(LIVES_BOX(hbox), 0);
6584     rdet->ofmt_combo = lives_standard_combo_new(NULL, ofmt, LIVES_BOX(hbox), NULL);
6585     lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
6586     lives_widget_set_halign(rdet->ofmt_combo, LIVES_ALIGN_CENTER);
6587     add_spring_to_box(LIVES_BOX(hbox), 0);
6588 
6589     lives_combo_populate(LIVES_COMBO(rdet->ofmt_combo), ofmt);
6590 
6591     lives_list_free_all(&ofmt);
6592 
6593     rdet->encoder_ofmt_fn = lives_signal_sync_connect_after(LIVES_COMBO(rdet->ofmt_combo), LIVES_WIDGET_CHANGED_SIGNAL,
6594                             LIVES_GUI_CALLBACK(on_encoder_ofmt_changed), rdet);
6595 
6596     widget_opts.justify = LIVES_JUSTIFY_CENTER;
6597 
6598     alabel = lives_standard_label_new(_("Audio format"));
6599     widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
6600 
6601     if (type != 1) {
6602       // add "Any" string
6603       lives_list_free_all(&prefs->acodec_list);
6604 
6605       prefs->acodec_list = lives_list_append(prefs->acodec_list, lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_ANY]));
6606       lives_box_pack_start(LIVES_BOX(vbox), alabel, FALSE, FALSE, widget_opts.packing_height);
6607       hbox = lives_hbox_new(FALSE, 0);
6608       add_spring_to_box(LIVES_BOX(hbox), 0);
6609       rdet->acodec_combo = lives_standard_combo_new(NULL, prefs->acodec_list, LIVES_BOX(hbox), NULL);
6610       lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
6611       lives_widget_set_halign(rdet->acodec_combo, LIVES_ALIGN_CENTER);
6612       add_spring_to_box(LIVES_BOX(hbox), 0);
6613     } else {
6614       add_fill_to_box(LIVES_BOX(vbox));
6615       lives_box_pack_start(LIVES_BOX(vbox), alabel, FALSE, FALSE, widget_opts.packing_height);
6616       lives_signal_handler_block(rdet->ofmt_combo, rdet->encoder_ofmt_fn);
6617       lives_combo_set_active_string(LIVES_COMBO(rdet->ofmt_combo), prefs->encoder.of_desc);
6618       lives_signal_handler_unblock(rdet->ofmt_combo, rdet->encoder_ofmt_fn);
6619 
6620       hbox = lives_hbox_new(FALSE, 0);
6621       add_spring_to_box(LIVES_BOX(hbox), 0);
6622       rdet->acodec_combo = lives_standard_combo_new(NULL, NULL, LIVES_BOX(hbox), NULL);
6623       lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
6624       lives_widget_set_halign(rdet->acodec_combo, LIVES_ALIGN_CENTER);
6625       add_spring_to_box(LIVES_BOX(hbox), 0);
6626 
6627       check_encoder_restrictions(TRUE, FALSE, TRUE);
6628       future_prefs->encoder.of_allowed_acodecs = prefs->encoder.of_allowed_acodecs;
6629       set_acodec_list_from_allowed(NULL, rdet);
6630     }
6631     add_fill_to_box(LIVES_BOX(vbox));
6632   } else vbox = top_vbox;
6633 
6634   //////////////// end expander section ///////////////////
6635   rdet->debug = NULL;
6636 
6637   if (type == 1 && prefs->show_dev_opts) {
6638     hbox = lives_hbox_new(FALSE, 0);
6639     //add_spring_to_box(LIVES_BOX(hbox), 0);
6640 
6641     rdet->debug = lives_standard_check_button_new((tmp = (_("Debug Mode"))), FALSE,
6642                   LIVES_BOX(hbox), (tmp2 = (_("Output diagnostic information to STDERR "
6643                                             "instead of to the GUI."))));
6644     lives_free(tmp);
6645     lives_free(tmp2);
6646 
6647     lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
6648     //lives_widget_set_halign(rdet->acodec_combo, LIVES_ALIGN_CENTER);
6649     add_spring_to_box(LIVES_BOX(hbox), 0);
6650   }
6651 
6652   add_fill_to_box(LIVES_BOX(dialog_vbox));
6653   cancelbutton = NULL;
6654 
6655   if (!(prefs->startup_interface == STARTUP_MT && !mainw->is_ready)) {
6656     cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(rdet->dialog), LIVES_STOCK_CANCEL, NULL,
6657                    LIVES_RESPONSE_CANCEL);
6658   } else if (LIVES_IS_BOX(daa)) add_fill_to_box(LIVES_BOX(daa));
6659 
6660   if (!(prefs->startup_interface == STARTUP_MT && !mainw->is_ready)) {
6661     if (type == 2 || type == 3) {
6662       if (CURRENT_CLIP_HAS_VIDEO && mainw->current_file != mainw->scrap_file && mainw->current_file != mainw->ascrap_file) {
6663         widget_opts.expand = LIVES_EXPAND_DEFAULT_HEIGHT | LIVES_EXPAND_EXTRA_WIDTH;
6664         rdet->usecur_button = lives_dialog_add_button_from_stock(LIVES_DIALOG(rdet->dialog),
6665                               NULL, _("_Set to current clip values"), LIVES_RESPONSE_RESET);
6666         widget_opts.expand = LIVES_EXPAND_DEFAULT;
6667         lives_signal_sync_connect(rdet->usecur_button, LIVES_WIDGET_CLICKED_SIGNAL, LIVES_GUI_CALLBACK(rdet_use_current),
6668                                   (livespointer)rdet);
6669       }
6670     }
6671   }
6672 
6673   widget_opts.expand = LIVES_EXPAND_DEFAULT_HEIGHT;
6674   if (type != 1) {
6675     rdet->okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(rdet->dialog), LIVES_STOCK_OK, NULL, LIVES_RESPONSE_OK);
6676   } else  {
6677     rdet->okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(rdet->dialog), LIVES_STOCK_GO_FORWARD, _("_Next"),
6678                      LIVES_RESPONSE_OK);
6679   }
6680   widget_opts.expand = LIVES_EXPAND_DEFAULT;
6681 
6682   lives_button_grab_default_special(rdet->okbutton);
6683 
6684   if (cancelbutton)
6685     lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, rdet_accel_group,
6686                                  LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
6687 
6688   if (!no_opts) {
6689     if (needs_new_encoder) {
6690       lives_widget_set_sensitive(rdet->okbutton, FALSE);
6691       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET); // force showing of transient window
6692       do_encoder_img_fmt_error(rdet);
6693     }
6694 
6695     lives_signal_sync_connect_after(LIVES_COMBO(rdet->acodec_combo), LIVES_WIDGET_CHANGED_SIGNAL,
6696                                     LIVES_GUI_CALLBACK(rdet_acodec_changed), rdet);
6697   }
6698 
6699   mainw->no_context_update = FALSE;
6700 
6701   if (type != 1) {
6702     // shrinkwrap to minimum
6703     spillover = lives_vbox_new(FALSE, 0);
6704     lives_box_pack_start(LIVES_BOX(top_vbox), spillover, TRUE, TRUE, 0); // mop up extra height
6705     lives_widget_show_all(rdet->dialog);
6706     lives_widget_context_update();
6707 
6708     height = lives_widget_get_allocation_height(scrollw) - (spht = lives_widget_get_allocation_height(spillover));
6709     width = lives_widget_get_allocation_width(scrollw);
6710 
6711     dheight = lives_widget_get_allocation_height(rdet->dialog) - spht;
6712     dwidth = lives_widget_get_allocation_width(rdet->dialog);
6713 
6714     if (dwidth > maxwidth) dwidth = maxwidth;
6715     if (dheight > maxheight) dheight = maxheight;
6716 
6717     if (width > dwidth) width = dwidth;
6718     if (height > dheight) height = dheight;
6719 
6720     lives_widget_destroy(spillover); // remove extra height
6721     lives_widget_process_updates(rdet->dialog);
6722     lives_widget_context_update();
6723 
6724     if (width > 0) lives_scrolled_window_set_min_content_width(LIVES_SCROLLED_WINDOW(scrollw), width);
6725     if (height > 0) lives_scrolled_window_set_min_content_height(LIVES_SCROLLED_WINDOW(scrollw), height);
6726     //lives_widget_set_size_request(scrollw, width, height);
6727     lives_widget_set_maximum_size(scrollw, width, height);
6728 
6729     if (dwidth < width + 6. * widget_opts.border_width) dwidth = width + 6. * widget_opts.border_width;
6730     if (dheight < height + 6. * widget_opts.border_width) dheight = height + 6. * widget_opts.border_width;
6731 
6732     lives_widget_set_size_request(rdet->dialog, dwidth, dheight);
6733     lives_widget_set_maximum_size(rdet->dialog, dwidth, dheight);
6734 
6735     // for expander, need to make it resizable
6736     lives_window_set_resizable(LIVES_WINDOW(rdet->dialog), TRUE);
6737   }
6738 
6739   lives_widget_show_all(rdet->dialog);
6740   return rdet;
6741 }
6742 
6743