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