1 // resample.c
2 // LiVES
3 // (c) G. Finch 2004 - 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 for reordering, resampling video and audio
8
9 #include "main.h"
10 #include "resample.h"
11 #include "callbacks.h"
12 #include "effects.h"
13 #include "audio.h"
14 #include "events.h"
15 #include "cvirtual.h"
16
17 static int reorder_width = 0;
18 static int reorder_height = 0;
19 static boolean reorder_leave_back = FALSE;
20
reorder_leave_back_set(boolean val)21 void reorder_leave_back_set(boolean val) {reorder_leave_back = val;}
22
23 /////////////////////////////////////////////////////
24
q_gint64(ticks_t in,double fps)25 LIVES_GLOBAL_INLINE ticks_t q_gint64(ticks_t in, double fps) {
26 // quantise timecode to fps
27 if (in > (ticks_t)0) return ((((double)((ticks_t)((double)in / (double)TICKS_PER_SECOND_DBL * (double)fps + .5))) /
28 (double)fps)) * TICKS_PER_SECOND_DBL + .5; // quantise to frame timing
29
30 if (in < (ticks_t)0) return ((((double)((ticks_t)((double)in / (double)TICKS_PER_SECOND_DBL * (double)fps - .5))) /
31 (double)fps)) * TICKS_PER_SECOND_DBL - .5; // quantise to frame timing
32 return (ticks_t)0;
33 }
34
q_gint64_floor(ticks_t in,double fps)35 LIVES_GLOBAL_INLINE ticks_t q_gint64_floor(ticks_t in, double fps) {
36 if (in != 0) return (double)((ticks_t)((double)in / TICKS_PER_SECOND_DBL * fps + .000001)) / fps *
37 TICKS_PER_SECOND_DBL; // quantise to frame timing
38 return 0;
39 }
40
q_dbl(double in,double fps)41 LIVES_GLOBAL_INLINE ticks_t q_dbl(double in, double fps) {
42 // quantise (double)in to fps
43 if (in > 0.) return ((ticks_t)((double)in * (double)fps + (double).5) / (double)fps) *
44 (ticks_t)TICKS_PER_SECOND; // quantise to frame timing
45 if (in < 0.) return ((ticks_t)((double)in * (double)fps - (double).5) / (double)fps) *
46 (ticks_t)TICKS_PER_SECOND; // quantise to frame timing
47 return (ticks_t)0;
48 }
49
50
51 /// convert seek time to an integer number of samples
quant_asamps(double seek,int arate)52 LIVES_GLOBAL_INLINE size_t quant_asamps(double seek, int arate) {
53 size_t samps = (seek <= 0. || arate <= 0) ? 0 : (size_t)(seek * (double)arate + .99999);
54 return samps;
55 }
56
57 /// convert seek time (secs) to an (almost) integer number of samples
quant_aseek(double seek,int arate)58 LIVES_GLOBAL_INLINE double quant_aseek(double seek, int arate) {
59 if (arate <= 0) return 0.;
60 else {
61 size_t samps = quant_asamps(seek, arate);
62 return (double)samps / (double)arate;
63 }
64 }
65
quant_abytes(double seek,int arate,int achans,int asampsize)66 LIVES_GLOBAL_INLINE off_t quant_abytes(double seek, int arate, int achans, int asampsize) {
67 size_t samps = quant_asamps(seek, arate);
68 return samps * (size_t)(achans * asampsize);
69 }
70
71
count_resampled_frames(int in_frames,double orig_fps,double resampled_fps)72 LIVES_GLOBAL_INLINE int count_resampled_frames(int in_frames, double orig_fps, double resampled_fps) {
73 int res_frames;
74 if (resampled_fps < orig_fps) return ((res_frames = (int)((double)in_frames / orig_fps * resampled_fps)) < 1) ? 1 : res_frames;
75 else return ((res_frames = (int)((double)in_frames / orig_fps * resampled_fps + .49999)) < 1) ? 1 : res_frames;
76 }
77
78 /////////////////////////////////////////////////////
79
auto_resample_resize(int width,int height,double fps,int fps_num,int fps_denom,int arate,int asigned,boolean swap_endian)80 boolean auto_resample_resize(int width, int height, double fps, int fps_num, int fps_denom, int arate,
81 int asigned, boolean swap_endian) {
82 // do a block atomic: resample audio, then resample video/resize or joint resample/resize
83
84 // TODO: check if we still need to letterbox here, or if the encoders handle that now
85
86 int current_file = mainw->current_file;
87 boolean audio_resampled = FALSE;
88 boolean video_resampled = FALSE;
89 boolean video_resized = FALSE;
90 boolean bad_header = FALSE;
91
92 int frames = cfile->frames;
93
94 reorder_leave_back = FALSE;
95
96 if (asigned != 0 || (arate > 0 && arate != cfile->arate) || swap_endian) {
97 cfile->undo1_int = arate;
98 cfile->undo2_int = cfile->achans;
99 cfile->undo3_int = cfile->asampsize;
100 cfile->undo1_uint = cfile->signed_endian;
101
102 if (asigned == 1 && (cfile->signed_endian & AFORM_UNSIGNED) == AFORM_UNSIGNED) cfile->undo1_uint ^= AFORM_UNSIGNED;
103 else if (asigned == 2 && (cfile->signed_endian & AFORM_UNSIGNED) != AFORM_UNSIGNED) cfile->undo1_uint |= AFORM_UNSIGNED;
104
105 if (swap_endian) {
106 if (cfile->signed_endian & AFORM_BIG_ENDIAN) cfile->undo1_uint ^= AFORM_BIG_ENDIAN;
107 else cfile->undo1_uint |= AFORM_BIG_ENDIAN;
108 }
109
110 on_resaudio_ok_clicked(NULL, NULL);
111 if (mainw->error) return FALSE;
112 audio_resampled = TRUE;
113 }
114
115 else {
116 cfile->undo1_int = cfile->arate;
117 cfile->undo2_int = cfile->achans;
118 cfile->undo3_int = cfile->asampsize;
119 cfile->undo4_int = cfile->arps;
120 cfile->undo1_uint = cfile->signed_endian;
121 }
122
123 if (fps_denom > 0) {
124 fps = (fps_num * 1.) / (fps_denom * 1.);
125 }
126 if (fps > 0. && fps != cfile->fps) {
127 // FPS CHANGE...
128 if ((width != cfile->hsize || height != cfile->vsize) && width * height > 0) {
129 // CHANGING SIZE..
130
131 if (fps > cfile->fps) {
132 // we will have more frames...
133 // ...do resize first
134 cfile->ohsize = cfile->hsize;
135 cfile->ovsize = cfile->vsize;
136
137 if (prefs->enc_letterbox) {
138 int iwidth = cfile->hsize, iheight = cfile->vsize;
139 calc_maxspect(width, height, &iwidth, &iheight);
140 width = iwidth;
141 height = iheight;
142 }
143
144 resize_all(mainw->current_file, width, height, cfile->img_type, TRUE, NULL, NULL);
145 realize_all_frames(mainw->current_file, _("Pulling frames"), FALSE);
146
147 cfile->hsize = width;
148 cfile->vsize = height;
149
150 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_WIDTH, &cfile->hsize)) bad_header = TRUE;
151 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_HEIGHT, &cfile->vsize)) bad_header = TRUE;
152 if (bad_header) do_header_write_error(mainw->current_file);
153
154 cfile->undo1_dbl = fps;
155 cfile->undo_start = 1;
156 cfile->undo_end = frames;
157
158 // now resample
159
160 // special "cheat" mode for LiVES
161 reorder_leave_back = TRUE;
162
163 reorder_width = width;
164 reorder_height = height;
165
166 mainw->resizing = TRUE;
167 on_resample_vid_ok(NULL, NULL);
168
169 reorder_leave_back = FALSE;
170 cfile->undo_action = UNDO_ATOMIC_RESAMPLE_RESIZE;
171 if (mainw->error) {
172 on_undo_activate(NULL, NULL);
173 return FALSE;
174 }
175
176 video_resized = TRUE;
177 video_resampled = TRUE;
178 } else {
179 // fewer frames
180 // do resample *with* resize
181 cfile->ohsize = cfile->hsize;
182 cfile->ovsize = cfile->vsize;
183 cfile->undo1_dbl = fps;
184
185 // another special "cheat" mode for LiVES
186 reorder_width = width;
187 reorder_height = height;
188
189 mainw->resizing = TRUE;
190 on_resample_vid_ok(NULL, NULL);
191 mainw->resizing = FALSE;
192
193 reorder_width = reorder_height = 0;
194 cfile->undo_action = UNDO_ATOMIC_RESAMPLE_RESIZE;
195 cfile->hsize = width;
196 cfile->vsize = height;
197
198 if (mainw->error) {
199 on_undo_activate(NULL, NULL);
200 return FALSE;
201 }
202 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_WIDTH, &cfile->hsize)) bad_header = TRUE;
203 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_HEIGHT, &cfile->vsize)) bad_header = TRUE;
204 if (bad_header) do_header_write_error(mainw->current_file);
205
206 video_resampled = TRUE;
207 video_resized = TRUE;
208 }
209 } else {
210 //////////////////////////////////////////////////////////////////////////////////
211 cfile->undo1_dbl = fps;
212 cfile->undo_start = 1;
213 cfile->undo_end = cfile->frames;
214
215 reorder_width = width;
216 reorder_height = height;
217
218 on_resample_vid_ok(NULL, NULL);
219
220 reorder_width = reorder_height = 0;
221 reorder_leave_back = FALSE;
222
223 if (audio_resampled) cfile->undo_action = UNDO_ATOMIC_RESAMPLE_RESIZE;
224 if (mainw->error) {
225 on_undo_activate(NULL, NULL);
226 return FALSE;
227 }
228 //////////////////////////////////////////////////////////////////////////////////////
229 video_resampled = TRUE;
230 }
231 } else {
232 // NO FPS CHANGE
233 if ((width != cfile->hsize || height != cfile->vsize) && width * height > 0) {
234 // no fps change - just a normal resize
235 cfile->undo_start = 1;
236 cfile->undo_end = cfile->frames;
237 if (prefs->enc_letterbox) {
238 int iwidth = cfile->hsize, iheight = cfile->vsize;
239 calc_maxspect(width, height, &iwidth, &iheight);
240 width = iwidth;
241 height = iheight;
242 }
243
244 resize_all(mainw->current_file, width, height, cfile->img_type, TRUE, NULL, NULL);
245 realize_all_frames(mainw->current_file, _("Pulling frames"), FALSE);
246
247 cfile->hsize = width;
248 cfile->vsize = height;
249
250 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_WIDTH, &cfile->hsize)) bad_header = TRUE;
251 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_HEIGHT, &cfile->vsize)) bad_header = TRUE;
252 if (bad_header) do_header_write_error(mainw->current_file);
253
254 if (audio_resampled) cfile->undo_action = UNDO_ATOMIC_RESAMPLE_RESIZE;
255 else {
256 cfile->undo_action = UNDO_RESIZABLE;
257 set_undoable(_("Resize"), TRUE);
258 }
259 video_resized = TRUE;
260 if (!mainw->multitrack) {
261 switch_to_file((mainw->current_file = 0), current_file);
262 }
263 }
264 }
265
266 if (cfile->undo_action == UNDO_ATOMIC_RESAMPLE_RESIZE) {
267 // just in case we missed anything...
268
269 set_undoable(_("Resample/Resize"), TRUE);
270 if (!video_resized) {
271 cfile->ohsize = cfile->hsize;
272 cfile->ovsize = cfile->vsize;
273 }
274 if (!video_resampled) {
275 cfile->undo1_dbl = cfile->fps;
276 }
277 cfile->undo_start = 1;
278 cfile->undo_end = frames;
279 }
280 return TRUE;
281 }
282
283
284 //////////////////////////////////////////////////////////////////
285
copy_with_check(weed_plant_t * event,weed_plant_t * out_list,weed_timecode_t tc,char * what,size_t bytes,weed_plant_t ** ret_event)286 static weed_plant_t *copy_with_check(weed_plant_t *event, weed_plant_t *out_list, weed_timecode_t tc, char *what, size_t bytes,
287 weed_plant_t **ret_event) {
288 LiVESResponseType response;
289 weed_plant_t *new_list;
290 do {
291 response = LIVES_RESPONSE_OK;
292 if (ret_event) *ret_event = NULL;
293 if ((new_list = event_copy_and_insert(event, tc, out_list, ret_event)) == NULL) {
294 response = do_memory_error_dialog(what, bytes);
295 }
296 } while (response == LIVES_RESPONSE_RETRY);
297 if (response == LIVES_RESPONSE_CANCEL) return NULL;
298 return new_list;
299 }
300
301 #define READJ_MAX 2.
302 #define READJ_MIN 0.1
303
304 #define SMTH_FRAME_LIM 8
305 #define SMTH_TC_LIM (0.5 * TICKS_PER_SECOND_DBL)
306
pre_analyse(weed_plant_t * elist)307 void pre_analyse(weed_plant_t *elist) {
308 // note, this only works when we have a single audio track
309 // if we have > 1 then we would need to do something extra, like averaging the deltas
310
311 // optionally we can also try to smooth the frames; if abs(nxt - prev) < lim, curr = av(prev, nxt)
312
313 weed_event_t *event = get_first_event(elist), *last = NULL, *xevent;
314 weed_event_t *pframev = NULL, *ppframev = NULL;
315 int pclip = 0, ppclip = 0;
316 int pframe = 0, ppframe = 0;
317 weed_timecode_t stc = 0, etc, tc, ptc = 0, pptc = 0;
318 lives_audio_track_state_t *ststate = NULL, *enstate;
319 ticks_t offs = 0;
320 int ev_api = 100;
321 int ntracks;
322
323 if (weed_plant_has_leaf(elist, WEED_LEAF_WEED_EVENT_API_VERSION))
324 ev_api = weed_get_int_value(elist, WEED_LEAF_WEED_EVENT_API_VERSION, NULL);
325
326 for (; event; event = get_next_event(event)) {
327 if (prefs->rr_pre_smooth) {
328 if (WEED_EVENT_IS_FRAME(event)) {
329 weed_timecode_t tc = weed_event_get_timecode(event);
330 int clip = get_frame_event_clip(event, 0);
331 frames_t frame = get_frame_event_frame(event, 0);
332 if (pframev && ppframev && pclip == clip && ppclip == clip) {
333 if (abs(frame - pframe) <= SMTH_FRAME_LIM && (tc - pptc) < SMTH_TC_LIM) {
334 double del1 = (double)(ptc - pptc);
335 double del2 = (double)(tc - ptc);
336 if (del1 * del2 >= 3.5) {
337 pframe = (frames_t)(((double)ppframe * del2 + (double)frame * del1) / (del1 + del2) + .5);
338 weed_set_int64_value(pframev, WEED_LEAF_FRAMES, pframe);
339 }
340 }
341 }
342 pptc = ptc; ptc = tc;
343 ppframe = pframe; pframe = frame;
344 ppclip = pclip; pclip = clip;
345 ppframev = pframev; pframev = event;
346 }
347 }
348
349 if (!WEED_EVENT_IS_AUDIO_FRAME(event)) continue;
350 if (!last) {
351 stc = weed_event_get_timecode(event);
352 ststate = audio_frame_to_atstate(event, &ntracks);
353 if (ntracks > 1) break;
354 last = event;
355 continue;
356 }
357
358 if (prefs->rr_qmode) continue;
359
360 // we know the velocity from last aud. event, and current seekpos
361 // thus we can easily calculate the theoretical time we should arrive at
362 // seekpoint, and scale all timecodes.accordingly
363
364 // after the final, we just add constat adj.
365
366 etc = weed_event_get_timecode(event);
367 enstate = audio_frame_to_atstate(event, &ntracks);
368 if (ntracks > 1) break;
369
370 if (ststate[0].vel != 0. && (enstate[0].vel != 0. || ev_api >= 122) && enstate[0].afile == ststate[0].afile) {
371 double dtime = (double)(etc - stc) / TICKS_PER_SECOND_DBL;
372
373 if (dtime <= READJ_MAX && dtime >= READJ_MIN) {
374 /// for older lists we didnt set the seek point at audio off, so ignore those
375 double tpos = ststate[0].seek + ststate[0].vel * dtime;
376 double ratio = fabs(enstate[0].seek - ststate[0].seek) / fabs(tpos - ststate[0].seek);
377 double dtime;
378 weed_timecode_t otc = 0, ntc = 0;
379 // now have calculated the ratio, we can backtrack to start audio event, and adjust tcs
380 // new_tc -> start_tc + diff * ratio
381 for (xevent = last; xevent != event; xevent = get_next_event(xevent)) {
382 otc = get_event_timecode(xevent);
383 dtime = (double)(otc - stc) / TICKS_PER_SECOND_DBL;
384 dtime *= ratio;
385 ntc = stc + offs + (ticks_t)(dtime * TICKS_PER_SECOND_DBL);
386 weed_event_set_timecode(xevent, ntc);
387 }
388 offs += ntc - otc;
389 }
390 }
391 /// offs is what we will add to remianing events when we hit the end
392 lives_free(ststate);
393 ststate = enstate;
394 last = event;
395 stc = etc;
396 }
397
398 lives_freep((void **)&ststate);
399
400 // we hit the end, just add offs
401 for (xevent = last; xevent; xevent = get_next_event(xevent)) {
402 tc = get_event_timecode(xevent);
403 weed_event_set_timecode(xevent, tc + offs);
404 }
405 }
406
407
408
409 /**
410 @brief quantise from event_list_t *in_list to *out_list at the new rate of qfps
411
412 This is called from 3 functions:
413 - before rendering a recorded event_list
414 - when entering multitrack, if the fps is mismatched
415 - when resampling a clip (only frame events)
416
417 @return new event list, or old event list if no changes are needed (i.e fps is already correct), or NULL on error
418
419 if old_list has no frames then we just return an empty list
420 otherwise returned list will always have at least one event (a frame at timecode 0).
421
422 if there is a timecode gap at the start of the old_list (i.e frame 1 has a non-zero timecode)
423 then this well be eliminated in the out iist; i.e. frame 1 in out_list always has a timecode of 0.
424
425 if the old event_list has a fixed fps, then we try to keep the duration as near as possible, so:
426
427 nframes_new = MAX(nframes_old / old_fps * new_fps , 1)
428 the value is rounded DOWN in the case that qfps < old_fps
429 and rounded to the nearest integer in the case that qfps > old_fps
430
431 e.g 210 frames @ 20.0 fps -> 157 frames @ 15.0 fps
432 -> 410 frames @ 39.0 fps
433 @see count_resmpled_frames()
434
435 i.e when the duration of the final frame is added, the total will always be >= duration of the old list
436
437
438 if old_list has no fixed fps, then new_frames = (timecode of last frame - timecode of first frame) * qfps
439 rounded UP.
440
441 /// algorithm:
442 /// - get tc of 1st frame event in in_list
443 /// tc of out_list starts at zero. If allow_gap is FALSE we add an offset_tc
444 /// so out_list 0 coincides with tc of 1st frame in in_list.
445 /// loop:
446 /// - advance in_list until either we hit a frame event, or tc of NEXT event > out_tc
447 /// -- if tc of next frame is <= out_tc, we continue
448 /// -- update the state to current event
449 ///
450 /// - apply in_list state at out_tc, interpolating between last (current) in frame and next in frame
451 /// - advance out_tc by 1. / out_fps
452 /// - goto loop
453
454 /// inserting from scrap_file, we cannot interpolate frame numbers. So we just insert nearest frame
455 */
quantise_events(weed_plant_t * in_list,double qfps,boolean allow_gap)456 weed_plant_t *quantise_events(weed_plant_t *in_list, double qfps, boolean allow_gap) {
457 weed_timecode_t out_tc = 0, offset_tc = 0, in_tc, laud_tc = 0, nx_tc;
458 weed_timecode_t end_tc;
459
460 weed_plant_t *out_list, *xout_list;
461 weed_event_t *naudio_event = NULL;
462 weed_event_t *frame_event = NULL, *nframe_event = NULL;
463 weed_event_t *last_frame_event;
464 weed_event_t *event, *newframe = NULL;
465 weed_event_t *init_event, *filter_map = NULL, *deinit_event;
466 weed_event_t *prev_aframe, *xprev_aframe;
467 weed_timecode_t recst_tc = 0;
468
469 LiVESResponseType response;
470 LiVESList *init_events = NULL, *deinit_events = NULL, *list;
471
472 ticks_t tl;
473 double *xaseeks = NULL, *naseeks = NULL, *naccels = NULL;
474 double old_fps;
475 char *what;
476
477 boolean interpolate = TRUE;
478 int *clips = NULL, *naclips = NULL, *nclips = NULL;
479 int64_t *frames = NULL, *nframes = NULL;
480 int *xaclips = NULL;
481
482 int tracks, ntracks = 0, natracks = 0, xatracks = 0;
483 int etype;
484 int is_final = 0;
485 int ev_api = 100;
486
487 register int i, j, k;
488
489 if (!in_list) return NULL;
490 if (qfps < 1.) return NULL;
491
492 old_fps = weed_get_double_value(in_list, WEED_LEAF_FPS, NULL);
493 if (old_fps == qfps) return in_list;
494
495 tl = (TICKS_PER_SECOND_DBL / qfps + .499999);
496 what = (_("quantising the event list"));
497
498 do {
499 response = LIVES_RESPONSE_OK;
500 /// copy metadata; we will change PREV, NEXT and FPS
501 out_list = weed_plant_copy(in_list);
502 if (!out_list) {
503 response = do_memory_error_dialog(what, 0);
504 }
505 } while (response == LIVES_RESPONSE_RETRY);
506 if (response == LIVES_RESPONSE_CANCEL) {
507 event_list_free(out_list);
508 lives_free(what);
509 return NULL;
510 }
511
512 if (weed_plant_has_leaf(in_list, WEED_LEAF_WEED_EVENT_API_VERSION))
513 ev_api = weed_get_int_value(in_list, WEED_LEAF_WEED_EVENT_API_VERSION, NULL);
514
515 if (old_fps == 0. && prefs->rr_super && (!prefs->rr_qmode || prefs->rr_pre_smooth)) {
516 /// in pre-analysis, we will look at the audio frames, and instead of correcting the audio veloicity, we will
517 /// attempt to slightly modify (scale) the frame timings such that the audio hits the precise seek point
518 pre_analyse(in_list);
519 }
520
521 weed_set_voidptr_value(out_list, WEED_LEAF_FIRST, NULL);
522 weed_set_voidptr_value(out_list, WEED_LEAF_LAST, NULL);
523 weed_set_double_value(out_list, WEED_LEAF_FPS, qfps);
524
525 event = get_first_event(in_list);
526 if (!event) goto q_done;
527
528 last_frame_event = get_last_frame_event(in_list);
529 if (!last_frame_event) goto q_done;
530
531 if (!allow_gap) offset_tc = get_event_timecode(get_first_frame_event(in_list));
532 else out_tc = get_event_timecode(get_first_frame_event(in_list));
533 end_tc = get_event_timecode(last_frame_event) - offset_tc;
534 end_tc = q_gint64(end_tc + tl, qfps);
535
536 // tl >>2 - make sure we don't round down
537 for (; out_tc < end_tc || event; out_tc = q_gint64(out_tc + tl + (tl >> 2), qfps)) {
538 weed_timecode_t stop_tc = out_tc + offset_tc;
539 if (out_tc > end_tc) out_tc = end_tc;
540 while (1) {
541 /// in this mode we walk the event_list until we pass the output time, keeping track of
542 /// state - frame and clip numbers, audio positions, param values, then insert everything at the output slot
543 /// - we also look at the next frame to decide how to proceed
544 /// - for normal clips and audio, we can interpolate between the two
545 /// - for the scrap file, we cannot interpolate, so we insert whichever frame is nearest, unless we already inserted
546 /// the last frame and the next frame would be dropped
547
548 /// values which we maintain: current init_events (cancelled by a deinit)
549 /// current filter map (cancelled by a new filter_map)
550 /// current deinits (cancelled by cancelling an init_event)
551 /// current param changes (cancelled by another pchange for same fx / param or a deinit)
552 /// audio seeks
553
554 /// events are added in the standard ordering, i.e filter_inits, param changes, filter map, frame, filter_deinits
555 if (event) in_tc = get_event_timecode(event);
556
557 if (event && (is_final == 2 || (in_tc <= stop_tc && is_final != 1))) {
558 /// update the state until we pass out_tc
559 etype = weed_event_get_type(event);
560 //g_print("got event type %d at tc %ld, out = %ld\n", etype, in_tc, out_tc);
561
562 switch (etype) {
563 case WEED_EVENT_TYPE_MARKER: {
564 int marker_type = weed_get_int_value(event, WEED_LEAF_LIVES_TYPE, NULL);
565 if (marker_type == EVENT_MARKER_BLOCK_START || marker_type == EVENT_MARKER_BLOCK_UNORDERED
566 || marker_type == EVENT_MARKER_RECORD_START) {
567 // if event_list started as a recording then this will have been set for previews, but we now need to discard it
568 interpolate = FALSE;
569 lives_freep((void **)&xaclips);
570 lives_freep((void **)&xaseeks);
571 lives_freep((void **)&naclips);
572 lives_freep((void **)&naseeks);
573 xatracks = natracks = 0;
574 lives_list_free(init_events);
575 lives_list_free(deinit_events);
576 init_events = deinit_events = NULL;
577 filter_map = NULL;
578 if (prefs->rr_super && prefs->rr_amicro)
579 recst_tc = get_event_timecode(event);
580 }
581 if ((allow_gap && marker_type == EVENT_MARKER_RECORD_START)
582 || marker_type == EVENT_MARKER_BLOCK_START || marker_type == EVENT_MARKER_BLOCK_UNORDERED) {
583 if (!(xout_list = copy_with_check(event, out_list, out_tc, what, 0, NULL))) {
584 event_list_free(out_list);
585 out_list = NULL;
586 goto q_done;
587 }
588 out_list = xout_list;
589 }
590 }
591 break;
592 case WEED_EVENT_TYPE_FRAME:
593 interpolate = TRUE;
594 nframe_event = get_next_frame_event(event);
595 if (!nframe_event) is_final = 1;
596
597 /// now we have a choice: we can either insert this frame at out_tc with the current fx state,
598 /// or with the state at out_tc
599 /// the difference is: either we force insertion of this frame now, with the current
600 /// filter state,
601 /// or we wait until we pass the slot and insert with the filter inits at that time
602 if (!prefs->rr_fstate) {
603 if (!is_final) {
604 /// force insertion by setting stop_tc to -1, thus all events will have a timecode
605 if (weed_event_get_timecode(nframe_event) > stop_tc) stop_tc = -1; // force insertion now
606 }
607 }
608 /* // interpolate unadded audio */
609 if (natracks > 0) {
610 // advance the seek value, so when we do add the audio vals, the seek is to the right time
611 // we use const. accel calculated when picked up
612 double dt = (double)(in_tc - laud_tc) / TICKS_PER_SECOND_DBL;
613 for (i = 0; i < natracks; i += 2) {
614 double vel = naseeks[i + 1];
615 if (naseeks[i + 1] != 0.) {
616 naseeks[i] += vel * dt;
617 }
618 }
619 }
620
621 if (WEED_EVENT_IS_AUDIO_FRAME(event)) {
622 // update unadded audio state (natracks) from in_list
623 // TODO: make use of aframe_to_atstate();
624 int *aclips;
625 double *aseeks;
626 int atracks = weed_frame_event_get_audio_tracks(event, &aclips, &aseeks);
627 for (i = 0; i < atracks; i += 2) {
628 for (j = 0; j < natracks; j += 2) {
629 if (naclips[j] == aclips[i]) {
630 // replace (superceded)
631 naclips[j + 1] = aclips[i + 1];
632 naseeks[j] = aseeks[i];
633 naseeks[j + 1] = aseeks[i + 1];
634 break;
635 }
636 }
637
638 if (j == natracks) {
639 natracks += 2;
640 // append
641 naclips = (int *)lives_realloc(naclips, natracks * sizint);
642 naseeks = (double *)lives_realloc(naseeks, natracks * sizdbl);
643 naccels = (double *)lives_realloc(naccels, (natracks >> 1) * sizdbl);
644 naclips[natracks - 2] = aclips[j];
645 naclips[natracks - 1] = aclips[j + 1];
646 naseeks[natracks - 2] = aseeks[j];
647 naseeks[natracks - 1] = aseeks[j + 1];
648 naccels[(natracks >> 1) - 1] = 0.;
649 }
650 }
651 lives_free(aclips);
652 lives_free(aseeks);
653 if (naudio_event == event) naudio_event = NULL;
654 }
655 /// laud_tc is last frame in_tc, frame_event is last frame
656 laud_tc = in_tc;
657 frame_event = event;
658 if (event == nframe_event) nframe_event = NULL;
659 break;
660 case WEED_EVENT_TYPE_FILTER_INIT:
661 // add to filter_inits list
662 weed_leaf_delete(event, WEED_LEAF_HOST_TAG);
663 init_events = lives_list_prepend(init_events, event);
664 break;
665 case WEED_EVENT_TYPE_FILTER_DEINIT:
666 /// if init_event is in list, discard it + this event
667 init_event = weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
668 for (list = init_events; list; list = list->next) {
669 if (list->data == init_event) {
670 if (list->prev) list->prev->next = list->next;
671 else init_events = list->next;
672 if (list->next) list->next->prev = list->prev;
673 list->next = list->prev = NULL;
674 lives_list_free(list);
675 break;
676 }
677 }
678 if (!list) {
679 weed_event_t *out_event = get_last_event(out_list);
680 init_event = weed_get_voidptr_value(out_event, WEED_LEAF_INIT_EVENT, NULL);
681 weed_leaf_dup(out_event, init_event, WEED_LEAF_IN_PARAMETERS);
682 if (!is_final) deinit_events = lives_list_prepend(deinit_events, event);
683 else {
684 //g_print("adding deinit at %lld\n", out_tc);
685 if (!(xout_list = copy_with_check(event, out_list, out_tc, what, 0, NULL))) {
686 event_list_free(out_list);
687 out_list = NULL;
688 goto q_done;
689 }
690 out_list = xout_list;
691 }
692 }
693 break;
694 case WEED_EVENT_TYPE_PARAM_CHANGE:
695 if (is_final) break;
696 /// param changes just get inserted at whatever timcode, as long as their init_event isnt in the "to be added" list
697 init_event = weed_get_voidptr_value(event, WEED_LEAF_INIT_EVENT, NULL);
698 for (list = init_events; list; list = list->next) {
699 if (list->data == init_event) break;
700 }
701 if (!list) {
702 void **pchanges;
703 weed_event_t *pch_event, *init_event, *pchange, *npchange;
704 int nchanges, pnum;
705 if (!(xout_list = copy_with_check(event, out_list, in_tc - offset_tc, what, 0, &pch_event))) {
706 event_list_free(out_list);
707 out_list = NULL;
708 goto q_done;
709 }
710 // now we need to set PREV_CHANGE and NEXT_CHANGE
711 // starting at init_event, we check all init pchanges until we fins the matching INDEX
712 // then follow the NEXT_CHANGE ptrs until we get to NULL
713 // then finally set NEXT_CHANGE to point to event, and PREV_CHANGE to point backwards
714
715 out_list = xout_list;
716
717 init_event = weed_get_voidptr_value(pch_event, WEED_LEAF_INIT_EVENT, NULL);
718 pchanges = weed_get_voidptr_array_counted(init_event, WEED_LEAF_IN_PARAMETERS, &nchanges);
719 pnum = weed_get_int_value(pch_event, WEED_LEAF_INDEX, NULL);
720 for (i = 0; i < nchanges; i++) {
721 pchange = (weed_event_t *)pchanges[i];
722 if (weed_get_int_value(pchange, WEED_LEAF_INDEX, NULL) == pnum) {
723 npchange = weed_get_voidptr_value((weed_plant_t *)pchange, WEED_LEAF_NEXT_CHANGE, NULL);
724 while (npchange) {
725 pchange = npchange;
726 npchange = weed_get_voidptr_value((weed_plant_t *)pchange, WEED_LEAF_NEXT_CHANGE, NULL);
727 }
728 weed_set_voidptr_value(pchange, WEED_LEAF_NEXT_CHANGE, pch_event);
729 weed_set_voidptr_value(pch_event, WEED_LEAF_PREV_CHANGE, pchange);
730 break;
731 }
732 }
733 lives_free(pchanges);
734 }
735 break;
736 case WEED_EVENT_TYPE_FILTER_MAP:
737 /// replace current filter map
738 filter_map = event;
739 break;
740 default:
741 /// probably a marker; ignore
742 break;
743 }
744 if (event) event = get_next_event(event);
745 } else {
746 weed_timecode_t frame_tc;
747 /// insert the state
748 if (init_events) {
749 void **pchanges;
750 weed_event_t *xinit_event;
751 int nchanges;
752 // insert filter_inits + init pchanges
753 for (list = init_events; list; list = list->next) {
754 init_event = (weed_event_t *)list->data;
755 //g_print("ins init %p\n", init_event);
756 if (!(xout_list = copy_with_check(init_event, out_list, out_tc, what, 0, &xinit_event))) {
757 event_list_free(out_list);
758 out_list = NULL;
759 lives_list_free(init_events);
760 goto q_done;
761 }
762 out_list = xout_list;
763 // insert init pchanges
764 pchanges = weed_get_voidptr_array_counted(init_event, WEED_LEAF_IN_PARAMETERS, &nchanges);
765 init_event = xinit_event;
766 for (i = 0; i < nchanges; i++) {
767 weed_event_t *pchange = (weed_event_t *)pchanges[i];
768 if (!(xout_list = copy_with_check(pchange, out_list, out_tc, what, 0, (weed_event_t **)&pchanges[i]))) {
769 event_list_free(out_list);
770 out_list = NULL;
771 lives_list_free(init_events);
772 lives_free(pchanges);
773 goto q_done;
774 }
775 out_list = xout_list;
776 }
777 weed_set_voidptr_array(init_event, WEED_LEAF_IN_PARAMETERS, nchanges, pchanges);
778 }
779 lives_list_free(init_events);
780 init_events = NULL;
781 lives_free(pchanges);
782 }
783
784 if (filter_map && !deinit_events) {
785 //g_print("ins filter map\n");
786 if (!(xout_list = copy_with_check(filter_map, out_list, out_tc, what, 0, NULL))) {
787 event_list_free(out_list);
788 out_list = NULL;
789 goto q_done;
790 }
791 out_list = xout_list;
792 filter_map = NULL;
793 }
794 /// INSERT A FRAME AT OUT_TC
795
796 tracks = weed_frame_event_get_tracks(frame_event, &clips, &frames);
797 frame_tc = get_event_timecode(frame_event);
798
799 // frame_event is always <= out_tc, nframe_event is always > out_tc
800 if (!nframe_event) nframe_event = get_next_frame_event(frame_event);
801 ntracks = weed_frame_event_get_tracks(nframe_event, &nclips, &nframes);
802 nx_tc = get_event_timecode(nframe_event);
803
804 if (nframe_event) {
805 if (mainw->scrap_file != -1 && (nclips[0] == mainw->scrap_file || clips[0] == mainw->scrap_file)) {
806 if (nx_tc - (out_tc + offset_tc) < out_tc + offset_tc - frame_tc) {
807 // scrap file
808 frame_event = nframe_event;
809 lives_free(clips);
810 lives_free(frames);
811 frames = nframes;
812 clips = nclips;
813 tracks = ntracks;
814 }
815 } else {
816 if (old_fps == 0. && prefs->rr_super && prefs->rr_qsmooth && interpolate) {
817 /// interpolate frames if possible
818 double ratio = (double)(out_tc - frame_tc) / (double)(nx_tc - frame_tc);
819 for (i = 0; i < tracks; i++) {
820 if (i >= ntracks) break;
821 if (clips[i] == nclips[i]) {
822 frames[i] = (int64_t)((double)frames[i] + (double)(nframes[i] - frames[i]) * ratio);
823 }
824 }
825 }
826 lives_free(nclips);
827 lives_free(nframes);
828 }
829 }
830
831 /// now we insert the frame
832 //g_print("frame with %d tracks %d %d going in at %ld\n", tracks, clips[0], frames[0], out_tc);
833 out_list = append_frame_event(out_list, out_tc, tracks, clips, frames);
834 /* if (!insert_frame_event_at(out_list, out_tc, tracks, clips, frames, &newframe)) { */
835 /* response = do_memory_error_dialog(what, 0); */
836 /* } */
837 newframe = get_last_event(out_list);
838 if (response == LIVES_RESPONSE_CANCEL) {
839 event_list_free(out_list);
840 lives_free(what);
841 return NULL;
842 }
843 if (weed_plant_has_leaf(frame_event, WEED_LEAF_HOST_SCRAP_FILE_OFFSET)) {
844 weed_leaf_dup(newframe, frame_event, WEED_LEAF_HOST_SCRAP_FILE_OFFSET);
845 }
846 weed_leaf_dup(newframe, frame_event, WEED_LEAF_OVERLAY_TEXT);
847
848 lives_freep((void **)&frames);
849 lives_freep((void **)&clips);
850
851 if (natracks > 0) {
852 /// insert the audio state
853 // filter naclips, remove any zeros for avel when track is not in xaclips
854 for (i = 0; i < natracks; i += 2) {
855 if (naseeks[i + 1] == 0.) {
856 for (j = 0; j < xatracks; j += 2) {
857 if (xaclips[j] == naclips[i] && xaseeks[j + 1] != 0.) break;
858 }
859 if (j == xatracks) {
860 natracks -= 2;
861 if (natracks == 0) {
862 lives_freep((void **)&naclips);
863 lives_freep((void **)&naseeks);
864 lives_freep((void **)&naccels);
865 } else {
866 for (k = i; k < natracks; k += 2) {
867 naclips[k] = naclips[k + 2];
868 naclips[k + 1] = naclips[k + 3];
869 naseeks[k] = naseeks[k + 2];
870 naseeks[k + 1] = naseeks[k + 3];
871 naccels[k >> 1] = naccels[(k >> 1) + 1];
872 }
873 naclips = (int *)lives_realloc(naclips, natracks * sizint);
874 naseeks = (double *)lives_realloc(naseeks, natracks * sizdbl);
875 naccels = (double *)lives_realloc(naccels, (natracks >> 1) * sizdbl);
876 // *INDENT-OFF*
877 }}}}}
878 // *INDENT-ON*
879
880 if (natracks > 0) {
881 /// if there is still audio to be added, update the seek posn to out_tc
882 /// laud_tc is last frame in_tc
883 double dt = (double)(out_tc + offset_tc - laud_tc) / TICKS_PER_SECOND_DBL;
884 for (i = 0; i < natracks; i += 2) {
885 double vel = naseeks[i + 1];
886 if (naseeks[i + 1] != 0.) {
887 int in_arate = mainw->files[naclips[i + 1]]->arps;
888 naseeks[i] += vel * dt;
889 naseeks[i] = quant_aseek(naseeks[i], in_arate);
890 }
891 }
892
893 weed_set_int_array(newframe, WEED_LEAF_AUDIO_CLIPS, natracks, naclips);
894 weed_set_double_array(newframe, WEED_LEAF_AUDIO_SEEKS, natracks, naseeks);
895
896 if (prefs->rr_super && prefs->rr_amicro) {
897 /// the timecode of each audio frame is adjusted to the quantised time, and we update the seek position accordingly
898 /// however, when playing back, any velocity change will come slightly later than when recorded; thus
899 /// the player seek pos will be slightly off.
900 /// To remedy this we can very slightly adjust the velocity at the prior frame, so that the seek is correct when
901 /// arriving at the current audio frame
902 double amicro_lim = 4. / qfps;
903 prev_aframe = get_prev_audio_frame_event(newframe);
904 if ((double)(out_tc - get_event_timecode(prev_aframe)) / TICKS_PER_SECOND_DBL <= amicro_lim) {
905 //while (prev_aframe && (double)(out_tc - get_event_timecode(prev_aframe)) / TICKS_PER_SECOND_DBL <= amicro_lim) {
906 for (i = 0; i < natracks; i += 2) {
907 // check each track in natracks (currently active) to see if it is also in xatracks (all active)
908 boolean gottrack = FALSE;
909 ///< audio was off, older lists didnt store the offset
910 if (naseeks[i + 1] == 0. && ev_api < 122) continue;
911 for (k = 0; k < xatracks; k += 2) {
912 if (xaclips[k] == naclips[i]) {
913 //. track is in xatracks, so there must be a prev audio frame for the track;
914 // if the clips match then we will find
915 // the audio frame event and maybe adjust the velocity
916 if (xaclips[k + 1] == naclips[i + 1]) gottrack = TRUE;
917 break;
918 }
919 }
920 if (!gottrack) continue;
921
922 /// find the prior audio frame for the track
923 xprev_aframe = prev_aframe;
924 if (1) {
925 //while (gottrack && xprev_aframe) {
926 weed_timecode_t ptc = get_event_timecode(xprev_aframe);
927 int *paclips;
928 double *paseeks;
929 int patracks;
930 if (ptc < recst_tc) break;
931
932 patracks = weed_frame_event_get_audio_tracks(xprev_aframe, &paclips, &paseeks);
933
934 for (j = 0; j < patracks; j += 2) {
935 if (paclips[j] == naclips[i]) {
936 if (paclips[j + 1] == naclips[i + 1]) {
937 if (paseeks[j + 1] != 0.) {
938 double dt = (double)(out_tc - ptc) / TICKS_PER_SECOND_DBL;
939 //if (dt > amicro_lim) continue;
940 //else {
941 if (1) {
942 /// what we will do here is insert an extra audio event at the previous out_frame.
943 /// the seek will be calculated from old_val, and we will adjust the velocity
944 /// so we hit the seek value at this frame
945 /// adjust velocity by seek_delta / frame_duration
946 int in_arate = mainw->files[naclips[i + 1]]->arps;
947 double nvel = (naseeks[i] - paseeks[j]) / dt, seek;
948
949 if (nvel * paseeks[j + 1] < 0.) break;
950
951 if (nvel > paseeks[j + 1]) {
952 if (nvel / paseeks[j + 1] > SKJUMP_THRESH_RATIO) nvel = paseeks[j + 1] * SKJUMP_THRESH_RATIO;
953 } else {
954 if (paseeks[j + 1] / nvel > SKJUMP_THRESH_RATIO) nvel = paseeks[j + 1] / SKJUMP_THRESH_RATIO;
955 }
956
957 insert_audio_event_at(xprev_aframe, paclips[j], paclips[j + 1], paseeks[j], nvel);
958 //} else {
959 // if velocity change is too great then we may adjust the seek a little instead
960 seek = paseeks[j] + nvel * dt;
961
962 if (naseeks[i] > seek) {
963 if (naseeks[i] > seek + SKJUMP_THRESH_SECS) seek = naseeks[i] + SKJUMP_THRESH_SECS;
964 } else {
965 if (naseeks[i] < seek - SKJUMP_THRESH_SECS) seek = naseeks[i] - SKJUMP_THRESH_SECS;
966 }
967 naseeks[i] = quant_aseek(seek, in_arate);
968 weed_set_double_array(newframe, WEED_LEAF_AUDIO_SEEKS, natracks, naseeks);
969 }
970 break;
971 // *INDENT-OFF*
972 }}}}
973 // *INDENT-ON*
974 lives_freep((void **)&paclips);
975 lives_freep((void **)&paseeks);
976 // *INDENT-OFF*
977 }}}}
978 // *INDENT-ON*
979
980 /// merge natracks with xatracks
981 for (i = 0; i < natracks; i += 2) {
982 for (j = 0; j < xatracks; j += 2) {
983 if (naclips[i] == xaclips[j]) {
984 xaclips[j + 1] = naclips[i + 1];
985 xaseeks[j] = naseeks[i];
986 xaseeks[j + 1] = naseeks[i + 1];
987 break;
988 }
989 }
990 if (j == xatracks) {
991 xatracks += 2;
992 xaclips = lives_realloc(xaclips, xatracks * sizint);
993 xaseeks = lives_realloc(xaseeks, xatracks * sizdbl);
994 xaclips[xatracks - 2] = naclips[i];
995 xaclips[xatracks - 1] = naclips[i + 1];
996 xaseeks[xatracks - 2] = naseeks[i];
997 xaseeks[xatracks - 1] = naseeks[i + 1];
998 }
999 }
1000 natracks = 0;
1001 lives_freep((void **)&naclips);
1002 lives_freep((void **)&naseeks);
1003 lives_freep((void **)&naccels);
1004 }
1005
1006 lives_freep((void **)&frames);
1007 lives_freep((void **)&clips);
1008
1009 /// frame insertion done
1010
1011 if (deinit_events) {
1012 // insert filter_deinits
1013 for (list = deinit_events; list; list = list->next) {
1014 deinit_event = (weed_event_t *)list->data;
1015 //g_print("ins deinit %p\n", deinit_event);
1016 if (!(xout_list = copy_with_check(deinit_event, out_list, out_tc, what, 0, NULL))) {
1017 event_list_free(out_list);
1018 out_list = NULL;
1019 goto q_done;
1020 }
1021 out_list = xout_list;
1022 }
1023 lives_list_free(deinit_events);
1024 deinit_events = NULL;
1025 }
1026
1027 if (filter_map) {
1028 //g_print("ins filter map\n");
1029 if (!(xout_list = copy_with_check(filter_map, out_list, out_tc, what, 0, NULL))) {
1030 event_list_free(out_list);
1031 out_list = NULL;
1032 goto q_done;
1033 }
1034 out_list = xout_list;
1035 filter_map = NULL;
1036 }
1037 if (is_final == 1) {
1038 is_final = 2;
1039 } else break; /// increase out_tc
1040 }
1041 } /// end of the in_list
1042 } /// end of out_list
1043
1044 //g_print("RES: %p and %ld, %ld\n", event, out_tc + tl, end_tc);
1045 if (filter_map) {
1046 // insert final filter_map
1047 if (!(xout_list = copy_with_check(filter_map, out_list, end_tc, what, 0, NULL))) {
1048 event_list_free(out_list);
1049 out_list = NULL;
1050 goto q_done;
1051 }
1052 out_list = xout_list;
1053 }
1054
1055 if (!get_first_frame_event(out_list)) {
1056 // make sure we have at least one frame
1057 if ((event = get_last_frame_event(in_list))) {
1058 do {
1059 response = LIVES_RESPONSE_OK;
1060 lives_freep((void **)&clips);
1061 lives_freep((void **)&frames);
1062 tracks = weed_frame_event_get_tracks(event, &clips, &frames);
1063 if (insert_frame_event_at(out_list, 0., tracks, clips, frames, NULL) == NULL) {
1064 response = do_memory_error_dialog(what, 0);
1065 }
1066 } while (response == LIVES_RESPONSE_RETRY);
1067 if (response == LIVES_RESPONSE_CANCEL) {
1068 event_list_free(out_list);
1069 out_list = NULL;
1070 goto q_done;
1071 }
1072 }
1073 }
1074
1075 /// for completeness we should add closers for all active audio tracks,
1076 /// however this will be done in event_list_rectify() when necessary (or ideally, the player would add the closers and
1077 /// record the offsets)
1078
1079 q_done:
1080 lives_list_free(init_events);
1081 lives_list_free(deinit_events);
1082 lives_free(what);
1083 reset_ttable();
1084 return out_list;
1085 }
1086
1087
1088 //////////////////////////////////////////////////////////////////
1089
on_reorder_activate(int rwidth,int rheight)1090 static void on_reorder_activate(int rwidth, int rheight) {
1091 char *msg;
1092
1093 uint32_t chk_mask = WARN_MASK_LAYOUT_ALTER_FRAMES | WARN_MASK_LAYOUT_ALTER_AUDIO;
1094 if (!check_for_layout_errors(NULL, mainw->current_file, 1, 0, &chk_mask)) {
1095 return;
1096 }
1097
1098 cfile->old_frames = cfile->frames;
1099
1100 // we do the reorder in reorder_frames()
1101 // this will clear event_list and set it in event_list_back
1102 if ((cfile->frames = reorder_frames(rwidth, rheight)) < 0) {
1103 // reordering error
1104 if (!(cfile->undo_action == UNDO_RESAMPLE)) {
1105 cfile->frames = -cfile->frames;
1106 }
1107 unbuffer_lmap_errors(FALSE);
1108 return;
1109 }
1110
1111 if (mainw->cancelled != CANCEL_NONE) {
1112 return;
1113 }
1114
1115 if (cfile->start > cfile->frames) {
1116 cfile->start = cfile->frames;
1117 }
1118
1119 if (cfile->end > cfile->frames) {
1120 cfile->end = cfile->frames;
1121 }
1122
1123 cfile->event_list = NULL;
1124 cfile->next_event = NULL;
1125
1126 save_clip_value(mainw->current_file, CLIP_DETAILS_FRAMES, &cfile->frames);
1127
1128 switch_to_file(mainw->current_file, mainw->current_file);
1129 if (mainw->current_file > 0) {
1130 d_print_done();
1131 msg = lives_strdup_printf(_("Length of video is now %d frames.\n"), cfile->frames);
1132 } else {
1133 msg = lives_strdup_printf(_("Clipboard was resampled to %d frames.\n"), cfile->frames);
1134 }
1135
1136 d_print(msg);
1137 lives_free(msg);
1138
1139 if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
1140
1141 if (mainw->sl_undo_mem && cfile->stored_layout_frame != 0) {
1142 // need to invalidate undo/redo stack, in case file was used in some layout undo
1143 stored_event_list_free_undos();
1144 }
1145 }
1146
1147
on_resample_audio_activate(LiVESMenuItem * menuitem,livespointer user_data)1148 void on_resample_audio_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1149 // show the playback rate - real audio rate is cfile->arps
1150 mainw->fx1_val = cfile->arate;
1151 mainw->fx2_val = cfile->achans;
1152 mainw->fx3_val = cfile->asampsize;
1153 mainw->fx4_val = cfile->signed_endian;
1154 resaudw = create_resaudw(1, NULL, NULL);
1155 lives_widget_show(resaudw->dialog);
1156 }
1157
1158
on_resaudio_ok_clicked(LiVESButton * button,LiVESEntry * entry)1159 void on_resaudio_ok_clicked(LiVESButton * button, LiVESEntry * entry) {
1160 char *com;
1161
1162 int arate, achans, asampsize, arps;
1163 int asigned = 1, aendian = 1;
1164 int cur_signed, cur_endian;
1165 int i;
1166
1167 if (button) {
1168 arps = arate = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_arate)));
1169 achans = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_achans)));
1170 asampsize = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)));
1171 if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned))) {
1172 asigned = 0;
1173 }
1174 if (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend))) {
1175 aendian = 0;
1176 }
1177
1178 lives_widget_destroy(resaudw->dialog);
1179 lives_widget_context_update();
1180 lives_free(resaudw);
1181
1182 if (arate <= 0) {
1183 // TODO - show error icon1280
1184 widget_opts.non_modal = TRUE;
1185 do_error_dialog(_("\n\nNew rate must be greater than 0\n"));
1186 widget_opts.non_modal = FALSE;
1187 return;
1188 }
1189 } else {
1190 // called from on_redo or other places
1191 arate = arps = cfile->undo1_int;
1192 achans = cfile->undo2_int;
1193 asampsize = cfile->undo3_int;
1194 asigned = !(cfile->undo1_uint & AFORM_UNSIGNED);
1195 aendian = !(cfile->undo1_uint & AFORM_BIG_ENDIAN);
1196 }
1197
1198 uint32_t chk_mask = WARN_MASK_LAYOUT_ALTER_FRAMES | WARN_MASK_LAYOUT_ALTER_AUDIO;
1199 if (!check_for_layout_errors(NULL, mainw->current_file, 1, 0, &chk_mask)) {
1200 return;
1201 }
1202
1203 // store old values for undo/redo
1204 cfile->undo1_int = cfile->arate;
1205 cfile->undo2_int = cfile->achans;
1206 cfile->undo3_int = cfile->asampsize;
1207 cfile->undo4_int = cfile->arps;
1208 cfile->undo1_uint = cfile->signed_endian;
1209
1210 cur_signed = !(cfile->signed_endian & AFORM_UNSIGNED);
1211 cur_endian = !(cfile->signed_endian & AFORM_BIG_ENDIAN);
1212
1213 if (!(arate == cfile->arate && arps == cfile->arps && achans == cfile->achans && asampsize == cfile->asampsize &&
1214 asigned == cur_signed && aendian == cur_endian)) {
1215 if (cfile->arps != cfile->arate) {
1216 double audio_stretch = (double)cfile->arps / (double)cfile->arate;
1217 // pb rate != real rate - stretch to pb rate and resample
1218 lives_rm(cfile->info_file);
1219 com = lives_strdup_printf("%s resample_audio \"%s\" %d %d %d %d %d %d %d %d %d %d %.4f", prefs->backend,
1220 cfile->handle, cfile->arps,
1221 cfile->achans, cfile->asampsize, cur_signed, cur_endian, arps, cfile->achans, cfile->asampsize,
1222 cur_signed, cur_endian, audio_stretch);
1223 lives_system(com, FALSE);
1224 if (THREADVAR(com_failed)) {
1225 unbuffer_lmap_errors(FALSE);
1226 return;
1227 }
1228 do_progress_dialog(TRUE, FALSE, _("Resampling audio")); // TODO - allow cancel ??
1229 lives_free(com);
1230 cfile->arate = cfile->arps = arps;
1231 } else {
1232 lives_rm(cfile->info_file);
1233 com = lives_strdup_printf("%s resample_audio \"%s\" %d %d %d %d %d %d %d %d %d %d", prefs->backend,
1234 cfile->handle, cfile->arps,
1235 cfile->achans, cfile->asampsize, cur_signed, cur_endian, arps, achans, asampsize,
1236 asigned, aendian);
1237 mainw->cancelled = CANCEL_NONE;
1238 mainw->error = FALSE;
1239 lives_rm(cfile->info_file);
1240 THREADVAR(com_failed) = FALSE;
1241 lives_system(com, FALSE);
1242 check_backend_return(cfile);
1243 if (THREADVAR(com_failed)) {
1244 unbuffer_lmap_errors(FALSE);
1245 return;
1246 }
1247 do_progress_dialog(TRUE, FALSE, _("Resampling audio"));
1248 lives_free(com);
1249 }
1250 }
1251
1252 if (cfile->audio_waveform) {
1253 for (i = 0; i < cfile->achans; lives_freep((void **)&cfile->audio_waveform[i++]));
1254 lives_freep((void **)&cfile->audio_waveform);
1255 lives_freep((void **)&cfile->aw_sizes);
1256 }
1257
1258 cfile->arate = arate;
1259 cfile->achans = achans;
1260 cfile->asampsize = asampsize;
1261 cfile->arps = arps;
1262 cfile->signed_endian = get_signed_endian(asigned, aendian);
1263 cfile->changed = TRUE;
1264
1265 cfile->undo_action = UNDO_AUDIO_RESAMPLE;
1266 mainw->error = FALSE;
1267 reget_afilesize(mainw->current_file);
1268
1269 if (cfile->afilesize == 0l) {
1270 widget_opts.non_modal = TRUE;
1271 do_error_dialog(_("LiVES was unable to resample the audio as requested.\n"));
1272 widget_opts.non_modal = FALSE;
1273 on_undo_activate(NULL, NULL);
1274 set_undoable(_("Resample Audio"), FALSE);
1275 mainw->error = TRUE;
1276 unbuffer_lmap_errors(FALSE);
1277 return;
1278 }
1279 set_undoable(_("Resample Audio"), !prefs->conserve_space);
1280
1281 save_clip_values(mainw->current_file);
1282
1283 switch_to_file(mainw->current_file, mainw->current_file);
1284
1285 d_print(""); // force printing of switch message
1286
1287 d_print(_("Audio was resampled to %d Hz, %d channels, %d bit"), arate, achans, asampsize);
1288
1289 if (cur_signed != asigned) {
1290 if (asigned == 1) {
1291 d_print(_(", signed"));
1292 } else {
1293 d_print(_(", unsigned"));
1294 }
1295 }
1296 if (cur_endian != aendian) {
1297 if (aendian == 1) {
1298 d_print(_(", little-endian"));
1299 } else {
1300 d_print(_(", big-endian"));
1301 }
1302 }
1303 d_print("\n");
1304
1305 if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
1306
1307 if (mainw->sl_undo_mem && cfile->stored_layout_audio > 0.) {
1308 // need to invalidate undo/redo stack, in case file was used in some layout undo
1309 stored_event_list_free_undos();
1310 }
1311 }
1312
1313
on_resaudw_achans_changed(LiVESWidget * widg,livespointer user_data)1314 static void on_resaudw_achans_changed(LiVESWidget * widg, livespointer user_data) {
1315 _resaudw *resaudw = (_resaudw *)user_data;
1316 //char *tmp;
1317
1318 if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(widg))) {
1319 lives_widget_set_sensitive(resaudw->rb_signed, FALSE);
1320 lives_widget_set_sensitive(resaudw->rb_unsigned, FALSE);
1321 lives_widget_set_sensitive(resaudw->rb_bigend, FALSE);
1322 lives_widget_set_sensitive(resaudw->rb_littleend, FALSE);
1323 lives_widget_set_sensitive(resaudw->entry_arate, FALSE);
1324 lives_widget_set_sensitive(resaudw->entry_asamps, FALSE);
1325 lives_widget_set_sensitive(resaudw->entry_achans, FALSE);
1326 if (prefsw) {
1327 lives_widget_set_sensitive(prefsw->pertrack_checkbutton, FALSE);
1328 lives_widget_set_sensitive(prefsw->backaudio_checkbutton, FALSE);
1329 } else if (rdet) {
1330 lives_widget_set_sensitive(rdet->pertrack_checkbutton, FALSE);
1331 lives_widget_set_sensitive(rdet->backaudio_checkbutton, FALSE);
1332 }
1333 } else {
1334 if (atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps))) != 8) {
1335 lives_widget_set_sensitive(resaudw->rb_bigend, TRUE);
1336 lives_widget_set_sensitive(resaudw->rb_littleend, TRUE);
1337 }
1338 lives_widget_set_sensitive(resaudw->entry_arate, TRUE);
1339 lives_widget_set_sensitive(resaudw->entry_asamps, TRUE);
1340 lives_widget_set_sensitive(resaudw->entry_achans, TRUE);
1341 if (prefsw) {
1342 lives_widget_set_sensitive(prefsw->pertrack_checkbutton, TRUE);
1343 lives_widget_set_sensitive(prefsw->backaudio_checkbutton, TRUE);
1344 }
1345 if (rdet) {
1346 lives_widget_set_sensitive(rdet->backaudio_checkbutton, TRUE);
1347 lives_widget_set_sensitive(rdet->pertrack_checkbutton, TRUE);
1348 }
1349 }
1350 }
1351
1352
on_resaudw_asamps_changed(LiVESWidget * irrelevant,livespointer rubbish)1353 void on_resaudw_asamps_changed(LiVESWidget * irrelevant, livespointer rubbish) {
1354 if (atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps))) == 8) {
1355 lives_widget_set_sensitive(resaudw->rb_bigend, FALSE);
1356 lives_widget_set_sensitive(resaudw->rb_littleend, FALSE);
1357 lives_widget_set_sensitive(resaudw->rb_signed, FALSE);
1358 lives_widget_set_sensitive(resaudw->rb_unsigned, TRUE);
1359 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned), TRUE);
1360 } else {
1361 lives_widget_set_sensitive(resaudw->rb_bigend, TRUE);
1362 lives_widget_set_sensitive(resaudw->rb_littleend, TRUE);
1363 if (atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps))) == 16) {
1364 lives_widget_set_sensitive(resaudw->rb_signed, TRUE);
1365 lives_widget_set_sensitive(resaudw->rb_unsigned, FALSE);
1366 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_signed), TRUE);
1367 }
1368 }
1369 }
1370
1371
on_resample_video_activate(LiVESMenuItem * menuitem,livespointer user_data)1372 void on_resample_video_activate(LiVESMenuItem * menuitem, livespointer user_data) {
1373 // change speed from the menu
1374 create_new_pb_speed(2);
1375 mainw->fx1_val = cfile->fps;
1376 }
1377
1378
on_resample_vid_ok(LiVESButton * button,LiVESEntry * entry)1379 void on_resample_vid_ok(LiVESButton * button, LiVESEntry * entry) {
1380 weed_plant_t *real_back_list = NULL;
1381 weed_plant_t *new_event_list = NULL;
1382 double oundo1_dbl = cfile->undo1_dbl;
1383 LiVESResponseType response;
1384 ticks_t in_time = 0;
1385 double old_fps = cfile->fps;
1386 char *msg;
1387 char *what;
1388 boolean ratio_fps;
1389 boolean bad_header = FALSE;
1390 int old_frames;
1391 int ostart = cfile->start;
1392 int oend = cfile->end;
1393
1394 mainw->error = FALSE;
1395
1396 if (button) {
1397 lives_general_button_clicked(button, NULL);
1398 if (mainw->fx1_val == 0.) mainw->fx1_val = 1.;
1399 } else {
1400 mainw->fx1_val = cfile->undo1_dbl;
1401 }
1402
1403 if (mainw->current_file < 0 || cfile->frames == 0) return;
1404
1405 if (mainw->fx1_val == cfile->fps && !cfile->event_list) return;
1406
1407 real_back_list = cfile->event_list;
1408 what = (_("creating the event list for resampling"));
1409
1410 if (!cfile->event_list) {
1411 /* new_event_list = lives_event_list_new(NULL, 0); */
1412 /* weed_set_double_value(new_event_list, WEED_LEAF_FPS, cfile->fps); */
1413 for (int64_t i64 = 1; i64 <= (int64_t)cfile->frames; i64++) {
1414 do {
1415 response = LIVES_RESPONSE_OK;
1416 new_event_list = append_frame_event(new_event_list, in_time, 1, &(mainw->current_file), &i64);
1417 if (!new_event_list) {
1418 response = do_memory_error_dialog(what, 0);
1419 }
1420 } while (response == LIVES_RESPONSE_RETRY);
1421 if (response == LIVES_RESPONSE_CANCEL) {
1422 lives_free(what);
1423 return;
1424 }
1425 in_time += (ticks_t)(1. / cfile->fps * TICKS_PER_SECOND_DBL + .5);
1426 }
1427 cfile->event_list = new_event_list;
1428 }
1429 cfile->undo1_dbl = cfile->fps;
1430
1431 if (cfile->event_list_back) event_list_free(cfile->event_list_back);
1432 cfile->event_list_back = cfile->event_list;
1433
1434 //QUANTISE
1435 new_event_list = quantise_events(cfile->event_list_back, mainw->fx1_val, real_back_list != NULL);
1436 cfile->event_list = new_event_list;
1437
1438 if (!real_back_list) event_list_free(cfile->event_list_back);
1439 cfile->event_list_back = NULL;
1440
1441 if (!cfile->event_list) {
1442 cfile->event_list = real_back_list;
1443 cfile->undo1_dbl = oundo1_dbl;
1444 mainw->error = TRUE;
1445 return;
1446 }
1447
1448 if (mainw->multitrack) return;
1449
1450 ratio_fps = check_for_ratio_fps(mainw->fx1_val);
1451
1452 // we have now quantised to fixed fps; we have come here from reorder
1453
1454 if (ratio_fps) {
1455 // got a ratio
1456 msg = lives_strdup_printf(_("Resampling video at %.8f frames per second..."), mainw->fx1_val);
1457 } else {
1458 msg = lives_strdup_printf(_("Resampling video at %.3f frames per second..."), mainw->fx1_val);
1459 }
1460 if (mainw->current_file > 0) {
1461 d_print(msg);
1462 }
1463 lives_free(msg);
1464
1465 old_frames = cfile->frames;
1466
1467 // must set these before calling reorder
1468 cfile->start = (int)((cfile->start - 1.) / old_fps * mainw->fx1_val + 1.);
1469 if ((cfile->end = (int)((cfile->end * mainw->fx1_val) / old_fps + .49999)) < cfile->start) cfile->end = cfile->start;
1470
1471 cfile->undo_action = UNDO_RESAMPLE;
1472 // REORDER
1473 // this calls reorder_frames, which sets event_list_back==event_list, and clears event_list
1474 on_reorder_activate(reorder_width, reorder_height);
1475
1476 if (cfile->frames <= 0 || mainw->cancelled != CANCEL_NONE) {
1477 // reordering error...
1478 cfile->event_list = real_back_list;
1479 if (cfile->event_list_back) event_list_free(cfile->event_list_back);
1480 cfile->event_list_back = NULL;
1481 cfile->frames = old_frames;
1482 cfile->start = ostart;
1483 cfile->end = oend;
1484 load_end_image(cfile->end);
1485 load_start_image(cfile->start);
1486 cfile->undo1_dbl = oundo1_dbl;
1487 sensitize();
1488 mainw->error = TRUE;
1489 widget_opts.non_modal = TRUE;
1490 if (cfile->frames < 0) do_error_dialog(_("Reordering error !\n"));
1491 widget_opts.non_modal = FALSE;
1492 return;
1493 }
1494
1495 if (cfile->event_list_back) event_list_free(cfile->event_list_back);
1496 cfile->event_list_back = real_back_list;
1497
1498 cfile->ratio_fps = ratio_fps;
1499 cfile->pb_fps = cfile->fps = mainw->fx1_val;
1500 cfile->old_frames = old_frames;
1501
1502 set_undoable(_("Resample"), TRUE);
1503 if (cfile->clip_type == CLIP_TYPE_FILE && cfile->ext_src) {
1504 lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
1505 double dfps = (double)cdata->fps;
1506 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &dfps)) bad_header = TRUE;
1507 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_FPS, &cfile->fps)) bad_header = TRUE;
1508 } else {
1509 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &cfile->fps)) bad_header = TRUE;
1510 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_FPS, &cfile->pb_fps)) bad_header = TRUE;
1511 }
1512
1513 if (bad_header) do_header_write_error(mainw->current_file);
1514
1515 switch_to_file(mainw->current_file, mainw->current_file);
1516 }
1517
1518
1519 ///////// GUI stuff /////////////////////////////////////////////////////
1520
create_resaudw(short type,render_details * rdet,LiVESWidget * top_vbox)1521 _resaudw *create_resaudw(short type, render_details * rdet, LiVESWidget * top_vbox) {
1522 // type 1 == resample
1523 // type 2 == insert silence
1524 // type 3 == enter multitrack
1525 // type 4 == prefs/multitrack
1526 // type 5 == new clip record/record to selection with no existing audio
1527 // type 6 == record to clip with no existing audio
1528 // type 7 == record to clip with existing audio (show time only)
1529 // type 8 == grab external window, with audio
1530 // type 9 == grab external, no audio
1531 // type 10 == change inside multitrack / render to clip (embedded) [resets to 3]
1532 // type 11 == rte audio gen as rfx
1533
1534 LiVESWidget *dialog_vbox = NULL;
1535 LiVESWidget *vboxx;
1536 LiVESWidget *frame;
1537 LiVESWidget *combo_entry2;
1538 LiVESWidget *combo_entry3;
1539 LiVESWidget *combo_entry1;
1540 LiVESWidget *vseparator;
1541 LiVESWidget *radiobutton_u1;
1542 LiVESWidget *radiobutton_s1;
1543 LiVESWidget *vbox;
1544 LiVESWidget *radiobutton_b1;
1545 LiVESWidget *radiobutton_l1;
1546 LiVESWidget *combo4;
1547 LiVESWidget *combo5;
1548 LiVESWidget *combo6;
1549 LiVESWidget *cancelbutton;
1550 LiVESWidget *okbutton;
1551 LiVESWidget *label;
1552 LiVESWidget *hseparator;
1553 LiVESWidget *radiobutton;
1554 LiVESWidget *hbox;
1555 LiVESWidget *hbox2;
1556
1557 LiVESAccelGroup *accel_group = NULL;
1558
1559 LiVESSList *s1_group = NULL;
1560 LiVESSList *e1_group = NULL;
1561 LiVESSList *s2_group = NULL;
1562 LiVESSList *e2_group = NULL;
1563 LiVESSList *rbgroup = NULL;
1564
1565 LiVESList *channels = NULL;
1566 LiVESList *sampsize = NULL;
1567 LiVESList *rate = NULL;
1568
1569 double secs = 0.;
1570
1571 char *tmp;
1572
1573 int hours = 0, mins = 0;
1574 int aendian;
1575
1576 boolean chans_fixed = FALSE;
1577 boolean is_8bit;
1578
1579 _resaudw *resaudw = (_resaudw *)(lives_malloc(sizeof(_resaudw)));
1580
1581 if (type == 10) {
1582 chans_fixed = TRUE;
1583 type = 3;
1584 }
1585
1586 if (type > 5 && type != 11 && mainw->rec_end_time != -1.) {
1587 hours = (int)(mainw->rec_end_time / 3600.);
1588 mins = (int)((mainw->rec_end_time - (hours * 3600.)) / 60.);
1589 secs = mainw->rec_end_time - hours * 3600. - mins * 60.;
1590 }
1591
1592 channels = lives_list_append(channels, (livespointer)"1");
1593 channels = lives_list_append(channels, (livespointer)"2");
1594
1595 sampsize = lives_list_append(sampsize, (livespointer)"8");
1596 sampsize = lives_list_append(sampsize, (livespointer)"16");
1597
1598 rate = lives_list_append(rate, (livespointer)"5512");
1599 rate = lives_list_append(rate, (livespointer)"8000");
1600 rate = lives_list_append(rate, (livespointer)"11025");
1601 rate = lives_list_append(rate, (livespointer)"22050");
1602 rate = lives_list_append(rate, (livespointer)"32000");
1603 rate = lives_list_append(rate, (livespointer)"44100");
1604 rate = lives_list_append(rate, (livespointer)"48000");
1605 rate = lives_list_append(rate, (livespointer)"88200");
1606 rate = lives_list_append(rate, (livespointer)"96000");
1607 rate = lives_list_append(rate, (livespointer)"128000");
1608
1609 if (type < 3 || type > 4) {
1610 char *title = NULL;
1611
1612 if (type == 1) {
1613 title = (_("Resample Audio"));
1614 } else if (type == 2) {
1615 title = (_("Insert Silence"));
1616 } else if (type == 5 || type == 11 || type == 6 || type == 7) {
1617 title = (_("New Clip Audio"));
1618 } else if (type == 9 || type == 8) {
1619 title = (_("External Clip Settings"));
1620 }
1621
1622 resaudw->dialog = lives_standard_dialog_new(title, FALSE, DEF_DIALOG_WIDTH, DEF_DIALOG_HEIGHT);
1623 lives_signal_handlers_disconnect_by_func(resaudw->dialog, LIVES_GUI_CALLBACK(return_true), NULL);
1624 lives_free(title);
1625
1626 accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
1627 lives_window_add_accel_group(LIVES_WINDOW(resaudw->dialog), accel_group);
1628
1629 dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(resaudw->dialog));
1630
1631 vboxx = lives_vbox_new(FALSE, 0);
1632
1633 lives_box_pack_start(LIVES_BOX(dialog_vbox), vboxx, TRUE, TRUE, 0);
1634 } else vboxx = top_vbox;
1635
1636 if (type == 1) {
1637 frame = lives_standard_frame_new(_("Current"), 0., FALSE);
1638
1639 lives_box_pack_start(LIVES_BOX(vboxx), frame, FALSE, TRUE, 0);
1640
1641 hbox2 = lives_hbox_new(FALSE, 0);
1642 lives_container_add(LIVES_CONTAINER(frame), hbox2);
1643 //lives_container_set_border_width(LIVES_CONTAINER(hbox2), widget_opts.packing_width);
1644
1645 tmp = lives_strdup_printf("%d", (int)mainw->fx1_val);
1646
1647 combo_entry2 = lives_standard_entry_new(_("Rate (Hz) "), tmp, 10, 6, LIVES_BOX(hbox2), NULL);
1648 lives_free(tmp);
1649
1650 lives_editable_set_editable(LIVES_EDITABLE(combo_entry2), FALSE);
1651 lives_widget_set_can_focus(combo_entry2, FALSE);
1652
1653 tmp = lives_strdup_printf("%d", (int)mainw->fx2_val);
1654 combo_entry3 = lives_standard_entry_new(_("Channels"), tmp, 6, 2, LIVES_BOX(hbox2), NULL);
1655 lives_free(tmp);
1656
1657 lives_editable_set_editable(LIVES_EDITABLE(combo_entry3), FALSE);
1658 lives_widget_set_can_focus(combo_entry3, FALSE);
1659
1660 tmp = lives_strdup_printf("%d", (int)mainw->fx3_val);
1661 combo_entry1 = lives_standard_entry_new(_("Sample Size "), tmp, 6, 2, LIVES_BOX(hbox2), NULL);
1662 lives_free(tmp);
1663
1664 lives_editable_set_editable(LIVES_EDITABLE(combo_entry1), FALSE);
1665 lives_widget_set_can_focus(combo_entry1, FALSE);
1666
1667 vseparator = lives_vseparator_new();
1668 lives_box_pack_start(LIVES_BOX(hbox2), vseparator, FALSE, FALSE, 0);
1669
1670 vbox = lives_vbox_new(FALSE, 0);
1671 lives_box_pack_start(LIVES_BOX(hbox2), vbox, FALSE, FALSE, 0);
1672
1673 hbox = lives_hbox_new(FALSE, 0);
1674 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1675
1676 radiobutton_s1 = lives_standard_radio_button_new(_("Signed"), &s1_group, LIVES_BOX(hbox), NULL);
1677
1678 hbox = lives_hbox_new(FALSE, 0);
1679 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1680
1681 radiobutton_u1 = lives_standard_radio_button_new(_("Unsigned"), &s1_group, LIVES_BOX(hbox), NULL);
1682
1683 aendian = mainw->fx4_val;
1684
1685 if (aendian & AFORM_UNSIGNED) {
1686 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(radiobutton_u1), TRUE);
1687 } else {
1688 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(radiobutton_s1), TRUE);
1689 }
1690
1691 lives_widget_set_sensitive(radiobutton_u1, FALSE);
1692 lives_widget_set_sensitive(radiobutton_s1, FALSE);
1693
1694 vseparator = lives_vseparator_new();
1695 lives_box_pack_start(LIVES_BOX(hbox2), vseparator, FALSE, FALSE, 0);
1696
1697 vbox = lives_vbox_new(FALSE, 0);
1698 lives_box_pack_start(LIVES_BOX(hbox2), vbox, FALSE, FALSE, 0);
1699
1700 hbox = lives_hbox_new(FALSE, 0);
1701 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1702
1703 radiobutton_l1 = lives_standard_radio_button_new(_("Little Endian"), &e1_group, LIVES_BOX(hbox), NULL);
1704
1705 hbox = lives_hbox_new(FALSE, 0);
1706 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1707
1708 radiobutton_b1 = lives_standard_radio_button_new(_("Big Endian"), &e1_group, LIVES_BOX(hbox), NULL);
1709
1710 if (aendian & AFORM_BIG_ENDIAN) {
1711 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(radiobutton_b1), TRUE);
1712 } else {
1713 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(radiobutton_l1), TRUE);
1714 }
1715
1716 lives_widget_set_sensitive(radiobutton_b1, FALSE);
1717 lives_widget_set_sensitive(radiobutton_l1, FALSE);
1718 }
1719
1720 resaudw->aud_checkbutton = NULL;
1721
1722 if (type < 9 || type == 11) {
1723 if (type >= 3 && type != 11) tmp = (_("Audio"));
1724 else if (type == 2) tmp = (_("New Audio Details"));
1725 else tmp = (_("New"));
1726
1727 frame = lives_standard_frame_new(tmp, 0., FALSE);
1728 lives_free(tmp);
1729
1730 if (type == 4) lives_box_pack_start(LIVES_BOX(vboxx), frame, FALSE, FALSE, widget_opts.packing_height);
1731 else lives_box_pack_start(LIVES_BOX(vboxx), frame, FALSE, TRUE, 0);
1732
1733 resaudw->vbox = lives_vbox_new(FALSE, 0);
1734 lives_container_add(LIVES_CONTAINER(frame), resaudw->vbox);
1735
1736 if (type > 2 && type < 5 && !chans_fixed) {
1737 resaudw->aud_hbox = lives_hbox_new(FALSE, 0);
1738 lives_box_pack_start(LIVES_BOX(resaudw->vbox), resaudw->aud_hbox, FALSE, FALSE, 0);
1739
1740 resaudw->aud_checkbutton = lives_standard_check_button_new(_("_Enable audio"), FALSE, LIVES_BOX(resaudw->aud_hbox), NULL);
1741
1742 if (rdet) lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton), rdet->achans > 0);
1743 else lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton),
1744 !mainw->multitrack ? prefs->mt_def_achans > 0 : cfile->achans > 0);
1745 if (type == 4) {
1746 lives_signal_sync_connect(LIVES_GUI_OBJECT(resaudw->aud_checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
1747 LIVES_GUI_CALLBACK(apply_button_set_enabled), NULL);
1748 }
1749 }
1750
1751 hbox2 = lives_hbox_new(FALSE, 0);
1752 lives_box_pack_start(LIVES_BOX(resaudw->vbox), hbox2, FALSE, FALSE, widget_opts.packing_height);
1753 lives_container_set_border_width(LIVES_CONTAINER(hbox2), widget_opts.border_width);
1754
1755 vbox = lives_vbox_new(FALSE, 0);
1756 lives_box_pack_start(LIVES_BOX(hbox2), vbox, FALSE, FALSE, widget_opts.packing_width);
1757
1758 hbox = lives_hbox_new(FALSE, 0);
1759 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1760 lives_container_set_border_width(LIVES_CONTAINER(hbox), widget_opts.border_width);
1761
1762 combo4 = lives_standard_combo_new(_("Rate (Hz) "), rate, LIVES_BOX(hbox), NULL);
1763
1764 resaudw->entry_arate = lives_combo_get_entry(LIVES_COMBO(combo4));
1765
1766 lives_entry_set_width_chars(LIVES_ENTRY(resaudw->entry_arate), 12);
1767 if (type == 7) lives_widget_set_sensitive(combo4, FALSE);
1768
1769 if (type < 3 || (type > 4 && type < 8) || type == 11) tmp = lives_strdup_printf("%d", (int)mainw->fx1_val);
1770 else if (type == 8) tmp = lives_strdup_printf("%d", DEFAULT_AUDIO_RATE);
1771 else if (type == 3) tmp = lives_strdup_printf("%d", rdet->arate);
1772 else tmp = (!mainw->multitrack || cfile->achans == 0)
1773 ? lives_strdup_printf("%d", prefs->mt_def_arate) : lives_strdup_printf("%d", cfile->arate);
1774 lives_entry_set_text(LIVES_ENTRY(resaudw->entry_arate), tmp);
1775 lives_free(tmp);
1776
1777 if (type == 4) {
1778 lives_signal_sync_connect(LIVES_GUI_OBJECT(combo4), LIVES_WIDGET_CHANGED_SIGNAL,
1779 LIVES_GUI_CALLBACK(apply_button_set_enabled), NULL);
1780 }
1781
1782 combo5 = lives_standard_combo_new((type >= 3 && type != 11 ? (_("_Channels")) : (_("Channels"))),
1783 channels, LIVES_BOX(hbox), NULL);
1784
1785 if (type == 7) lives_widget_set_sensitive(combo5, FALSE);
1786
1787 resaudw->entry_achans = lives_combo_get_entry(LIVES_COMBO(combo5));
1788 lives_entry_set_width_chars(LIVES_ENTRY(resaudw->entry_achans), 8);
1789
1790 if (type < 3 || (type > 4 && type < 8) || type == 11) tmp = lives_strdup_printf("%d", (int)mainw->fx2_val);
1791 else if (type == 8) tmp = lives_strdup_printf("%d", DEFAULT_AUDIO_CHANS);
1792 else if (type == 3) tmp = lives_strdup_printf("%d", rdet->achans);
1793 else tmp = lives_strdup_printf("%d", (!mainw->multitrack || cfile->achans == 0)
1794 ? (prefs->mt_def_achans == 0 ? DEFAULT_AUDIO_CHANS
1795 : prefs->mt_def_achans) : cfile->achans);
1796 lives_entry_set_text(LIVES_ENTRY(resaudw->entry_achans), tmp);
1797 lives_free(tmp);
1798
1799 if (chans_fixed) {
1800 lives_widget_set_sensitive(resaudw->entry_achans, FALSE);
1801 lives_widget_set_sensitive(combo5, FALSE);
1802 }
1803
1804 if (type == 4) {
1805 lives_signal_sync_connect(LIVES_GUI_OBJECT(combo5), LIVES_WIDGET_CHANGED_SIGNAL,
1806 LIVES_GUI_CALLBACK(apply_button_set_enabled), NULL);
1807 }
1808
1809 combo6 = lives_standard_combo_new((type >= 3 && type != 11 ? (_("_Sample Size")) : (_("Sample Size"))),
1810 sampsize, LIVES_BOX(hbox), NULL);
1811
1812 if (type == 7) lives_widget_set_sensitive(combo6, FALSE);
1813
1814 resaudw->entry_asamps = lives_combo_get_entry(LIVES_COMBO(combo6));
1815 lives_entry_set_max_length(LIVES_ENTRY(resaudw->entry_asamps), 2);
1816 lives_editable_set_editable(LIVES_EDITABLE(resaudw->entry_asamps), FALSE);
1817 lives_entry_set_width_chars(LIVES_ENTRY(resaudw->entry_asamps), 8);
1818
1819 if (type < 3 || (type > 4 && type < 8) || type == 11) tmp = lives_strdup_printf("%d", (int)mainw->fx3_val);
1820 else if (type == 8) tmp = lives_strdup_printf("%d", DEFAULT_AUDIO_SAMPS);
1821 else if (type == 3) tmp = lives_strdup_printf("%d", rdet->asamps);
1822 else tmp = lives_strdup_printf("%d", (!mainw->multitrack || cfile->achans == 0) ? prefs->mt_def_asamps : cfile->asampsize);
1823 lives_entry_set_text(LIVES_ENTRY(resaudw->entry_asamps), tmp);
1824
1825 if (!strcmp(tmp, "8")) is_8bit = TRUE;
1826 else is_8bit = FALSE;
1827
1828 lives_free(tmp);
1829
1830 if (type == 4) {
1831 lives_signal_sync_connect(LIVES_GUI_OBJECT(combo6), LIVES_WIDGET_CHANGED_SIGNAL,
1832 LIVES_GUI_CALLBACK(apply_button_set_enabled), NULL);
1833 }
1834
1835 vbox = lives_vbox_new(FALSE, 0);
1836 if (type != 4) lives_box_pack_start(LIVES_BOX(hbox2), vbox, FALSE, FALSE, 0);
1837
1838 hbox = lives_hbox_new(FALSE, 0);
1839 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1840
1841 resaudw->rb_signed = lives_standard_radio_button_new(_("Signed"), &s2_group, LIVES_BOX(hbox), NULL);
1842
1843 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_signed), TRUE);
1844 if (type == 7 || is_8bit) lives_widget_set_sensitive(resaudw->rb_signed, FALSE);
1845
1846 hbox = lives_hbox_new(FALSE, 0);
1847 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1848
1849 resaudw->rb_unsigned = lives_standard_radio_button_new(_("Unsigned"), &s2_group, LIVES_BOX(hbox), NULL);
1850
1851 if (type == 7 || !is_8bit) lives_widget_set_sensitive(resaudw->rb_unsigned, FALSE);
1852
1853 if (type < 3 || (type > 4 && type < 8) || type == 11) aendian = mainw->fx4_val;
1854 else if (type == 8) aendian = DEFAULT_AUDIO_SIGNED16 | ((capable->byte_order == LIVES_BIG_ENDIAN) ? AFORM_BIG_ENDIAN : 0);
1855 else if (type == 3) aendian = rdet->aendian;
1856 else aendian = (!mainw->multitrack || cfile->achans == 0) ? prefs->mt_def_signed_endian : cfile->signed_endian;
1857
1858 if (aendian & AFORM_UNSIGNED) {
1859 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned), TRUE);
1860 } else {
1861 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_signed), TRUE);
1862 }
1863
1864 if (type == 4) {
1865 lives_signal_sync_connect(LIVES_GUI_OBJECT(resaudw->rb_signed), LIVES_WIDGET_TOGGLED_SIGNAL,
1866 LIVES_GUI_CALLBACK(apply_button_set_enabled), NULL);
1867 lives_signal_sync_connect(LIVES_GUI_OBJECT(resaudw->rb_unsigned), LIVES_WIDGET_TOGGLED_SIGNAL,
1868 LIVES_GUI_CALLBACK(apply_button_set_enabled), NULL);
1869 }
1870
1871 vbox = lives_vbox_new(FALSE, 0);
1872 lives_box_pack_start(LIVES_BOX(hbox2), vbox, FALSE, FALSE, 0);
1873
1874 hbox = lives_hbox_new(FALSE, 0);
1875 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1876
1877 resaudw->rb_littleend = lives_standard_radio_button_new(_("Little Endian"), &e2_group, LIVES_BOX(hbox), NULL);
1878
1879 if (type == 7) lives_widget_set_sensitive(resaudw->rb_littleend, FALSE);
1880
1881 hbox = lives_hbox_new(FALSE, 0);
1882 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1883
1884 resaudw->rb_bigend = lives_standard_radio_button_new(_("Big Endian"), &e2_group, LIVES_BOX(hbox), NULL);
1885
1886 if (type == 7) lives_widget_set_sensitive(resaudw->rb_bigend, FALSE);
1887
1888 if (aendian & AFORM_BIG_ENDIAN) {
1889 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend), TRUE);
1890 } else {
1891 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_littleend), TRUE);
1892 }
1893
1894 if (!strcmp(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)), "8")) {
1895 lives_widget_set_sensitive(resaudw->rb_littleend, FALSE);
1896 lives_widget_set_sensitive(resaudw->rb_bigend, FALSE);
1897 }
1898
1899 lives_signal_sync_connect(LIVES_GUI_OBJECT(resaudw->entry_asamps), LIVES_WIDGET_CHANGED_SIGNAL,
1900 LIVES_GUI_CALLBACK(on_resaudw_asamps_changed), NULL);
1901 }
1902
1903 if (type == 4) {
1904 lives_signal_sync_connect(LIVES_GUI_OBJECT(resaudw->rb_littleend), LIVES_WIDGET_TOGGLED_SIGNAL,
1905 LIVES_GUI_CALLBACK(apply_button_set_enabled), NULL);
1906 lives_signal_sync_connect(LIVES_GUI_OBJECT(resaudw->rb_bigend), LIVES_WIDGET_TOGGLED_SIGNAL,
1907 LIVES_GUI_CALLBACK(apply_button_set_enabled), NULL);
1908 }
1909
1910 if (type > 7 && type != 11) {
1911 frame = lives_standard_frame_new(_("Video"), 0., FALSE);
1912 lives_box_pack_start(LIVES_BOX(vboxx), frame, TRUE, TRUE, 0);
1913
1914 hbox = lives_hbox_new(FALSE, 0);
1915 lives_container_add(LIVES_CONTAINER(frame), hbox);
1916 lives_container_set_border_width(LIVES_CONTAINER(hbox), widget_opts.border_width);
1917
1918 resaudw->fps_spinbutton = lives_standard_spin_button_new(_("_Frames Per Second "),
1919 prefs->default_fps, 1., FPS_MAX, 1., 1., 3, LIVES_BOX(hbox), NULL);
1920 }
1921
1922 if (type > 4 && type != 11) {
1923 lives_box_set_spacing(LIVES_BOX(dialog_vbox), widget_opts.packing_height * 3);
1924
1925 hbox = lives_hbox_new(FALSE, 0);
1926 lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, TRUE, TRUE, widget_opts.packing_height);
1927
1928 if (type != 6 && type != 7) {
1929 radiobutton = lives_standard_radio_button_new(_("Record for maximum: "), &rbgroup, LIVES_BOX(hbox), NULL);
1930
1931 resaudw->hour_spinbutton = lives_standard_spin_button_new(_(" hours "), hours,
1932 0., hours > 23 ? hours : 23, 1., 1., 0, LIVES_BOX(hbox), NULL);
1933
1934 resaudw->minute_spinbutton = lives_standard_spin_button_new(_(" minutes "), mins,
1935 0., 59., 1., 10., 0, LIVES_BOX(hbox), NULL);
1936
1937 resaudw->second_spinbutton = lives_standard_spin_button_new(_(" seconds "), secs,
1938 0., 59., 1., 10., 0, LIVES_BOX(hbox), NULL);
1939
1940 hbox = lives_hbox_new(FALSE, 0);
1941 lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, TRUE, TRUE, widget_opts.packing_height);
1942
1943 resaudw->unlim_radiobutton = lives_standard_radio_button_new(_("Unlimited"), &rbgroup, LIVES_BOX(hbox), NULL);
1944
1945 lives_signal_sync_connect(LIVES_GUI_OBJECT(radiobutton), LIVES_WIDGET_TOGGLED_SIGNAL,
1946 LIVES_GUI_CALLBACK(on_rb_audrec_time_toggled),
1947 (livespointer)resaudw);
1948
1949 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->unlim_radiobutton), (type == 5 || type > 7) && type != 11);
1950
1951 }
1952
1953 if (type < 8 || type == 11) {
1954 hseparator = lives_hseparator_new();
1955 lives_box_pack_start(LIVES_BOX(dialog_vbox), hseparator, TRUE, TRUE, 0);
1956
1957 label = lives_standard_label_new(_("Click OK to begin recording, or Cancel to quit."));
1958
1959 lives_box_pack_start(LIVES_BOX(dialog_vbox), label, TRUE, TRUE, 0);
1960 }
1961 }
1962
1963 if (type < 3 || type > 4) {
1964 cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(resaudw->dialog), LIVES_STOCK_CANCEL, NULL,
1965 LIVES_RESPONSE_CANCEL);
1966
1967 if (accel_group) lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
1968 LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
1969
1970
1971 okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(resaudw->dialog), LIVES_STOCK_OK, NULL,
1972 LIVES_RESPONSE_OK);
1973
1974 lives_button_grab_default_special(okbutton);
1975
1976 if (type < 8 || type == 11) {
1977 lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
1978 LIVES_GUI_CALLBACK(lives_general_button_clicked), resaudw);
1979
1980 if (type == 1) {
1981 lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
1982 LIVES_GUI_CALLBACK(on_resaudio_ok_clicked), NULL);
1983 } else if (type == 2 || type == 11) {
1984 lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
1985 LIVES_GUI_CALLBACK(on_ins_silence_details_clicked), NULL);
1986 } else if (type == 5) {
1987 lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
1988 LIVES_GUI_CALLBACK(on_recaudclip_ok_clicked),
1989 LIVES_INT_TO_POINTER(0));
1990 } else if (type == 6 || type == 7) {
1991 lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
1992 LIVES_GUI_CALLBACK(on_recaudclip_ok_clicked),
1993 LIVES_INT_TO_POINTER(1));
1994 }
1995
1996 }
1997
1998 lives_widget_show_all(resaudw->dialog);
1999 } else {
2000 if (resaudw->aud_checkbutton) {
2001 lives_signal_sync_connect_after(LIVES_GUI_OBJECT(resaudw->aud_checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2002 LIVES_GUI_CALLBACK(on_resaudw_achans_changed), (livespointer)resaudw);
2003 on_resaudw_achans_changed(resaudw->aud_checkbutton, (livespointer)resaudw);
2004 }
2005 }
2006
2007 lives_widget_show_all(vboxx);
2008
2009 lives_list_free(channels);
2010 lives_list_free(sampsize);
2011 lives_list_free(rate);
2012
2013 return resaudw;
2014 }
2015
2016
on_change_speed_activate(LiVESMenuItem * menuitem,livespointer user_data)2017 void on_change_speed_activate(LiVESMenuItem * menuitem, livespointer user_data) {
2018 // change speed from the menu
2019 create_new_pb_speed(1);
2020 mainw->fx1_bool = mainw->fx2_bool = FALSE;
2021 mainw->fx1_val = cfile->fps;
2022 }
2023
2024
on_change_speed_ok_clicked(LiVESButton * button,livespointer user_data)2025 void on_change_speed_ok_clicked(LiVESButton * button, livespointer user_data) {
2026 double arate = cfile->arate / cfile->fps;
2027 char *msg;
2028 boolean bad_header = FALSE;
2029 //int new_frames = count_resampled_frames(cfile->frames, mainw->fx1_val, cfile->fps);
2030
2031 // change playback rate
2032 if (button) {
2033 lives_general_button_clicked(button, NULL);
2034 }
2035
2036 if (mainw->fx2_bool) {
2037 mainw->fx1_val = (double)((int)((double)cfile->frames / mainw->fx2_val * 1000. + .5)) / 1000.;
2038 if (mainw->fx1_val < 1.) mainw->fx1_val = 1.;
2039 if (mainw->fx1_val > FPS_MAX) mainw->fx1_val = FPS_MAX;
2040 }
2041
2042 char *tmp = (_("Changing the clip fps"));
2043 uint32_t chk_mask = WARN_MASK_LAYOUT_DELETE_FRAMES | WARN_MASK_LAYOUT_SHIFT_FRAMES
2044 | WARN_MASK_LAYOUT_ALTER_FRAMES;
2045 if (mainw->fx1_bool) chk_mask |= WARN_MASK_LAYOUT_DELETE_AUDIO | WARN_MASK_LAYOUT_SHIFT_AUDIO
2046 | WARN_MASK_LAYOUT_ALTER_AUDIO;
2047 if (!check_for_layout_errors(tmp, mainw->current_file, 1, 0, &chk_mask)) {
2048 lives_free(tmp);
2049 return;
2050 }
2051 lives_free(tmp);
2052
2053 if (button == NULL) {
2054 mainw->fx1_bool = !(cfile->undo1_int == cfile->arate);
2055 mainw->fx1_val = cfile->undo1_dbl;
2056 }
2057
2058 set_undoable(_("Speed Change"), TRUE);
2059 cfile->undo1_dbl = cfile->fps;
2060 cfile->undo1_int = cfile->arate;
2061 cfile->undo_action = UNDO_CHANGE_SPEED;
2062
2063 if (mainw->fx1_val == 0.) mainw->fx1_val = 1.;
2064
2065 // update the frame rate
2066 cfile->pb_fps = cfile->fps = mainw->fx1_val;
2067
2068 lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), cfile->pb_fps);
2069
2070 if (mainw->fx1_bool) {
2071 cfile->arate = (int)(arate * cfile->fps + .5);
2072 msg = lives_strdup_printf(_("Changed playback speed to %.3f frames per second and audio to %d Hz.\n"), cfile->fps,
2073 cfile->arate);
2074 } else {
2075 msg = lives_strdup_printf(_("Changed playback speed to %.3f frames per second.\n"), cfile->fps);
2076 }
2077 d_print(msg);
2078 lives_free(msg);
2079
2080 cfile->ratio_fps = FALSE;
2081
2082 if (cfile->clip_type == CLIP_TYPE_FILE && cfile->ext_src) {
2083 lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
2084 double dfps = (double)cdata->fps;
2085 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &dfps)) bad_header = TRUE;
2086 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_FPS, &cfile->fps)) bad_header = TRUE;
2087 } else {
2088 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_FPS, &cfile->fps)) bad_header = TRUE;
2089 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_FPS, &cfile->pb_fps)) bad_header = TRUE;
2090 }
2091
2092 if (!save_clip_value(mainw->current_file, CLIP_DETAILS_PB_ARATE, &cfile->arate)) bad_header = TRUE;
2093 if (bad_header) do_header_write_error(mainw->current_file);
2094
2095 switch_to_file(mainw->current_file, mainw->current_file);
2096
2097 if (chk_mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(chk_mask));
2098
2099 if (mainw->sl_undo_mem && cfile->stored_layout_frame != 0) {
2100 // need to invalidate undo/redo stack, in case file was used in some layout undo
2101 stored_event_list_free_undos();
2102 }
2103 }
2104
2105
reorder_frames(int rwidth,int rheight)2106 int reorder_frames(int rwidth, int rheight) {
2107 int new_frames = cfile->old_frames;
2108 int cur_frames = cfile->frames;
2109 char **array;
2110 char *com;
2111
2112 if (rwidth * rheight == 0) com = lives_strdup_printf("%s reorder \"%s\" \"%s\" %d 0 0 %d %d", prefs->backend, cfile->handle,
2113 get_image_ext_for_type(cfile->img_type), !mainw->endian,
2114 reorder_leave_back, cfile->frames);
2115 else {
2116 if (!prefs->enc_letterbox) {
2117 com = lives_strdup_printf("%s reorder \"%s\" \"%s\" %d %d %d 0 %d", prefs->backend, cfile->handle,
2118 get_image_ext_for_type(cfile->img_type), !mainw->endian, rwidth, rheight, cfile->frames);
2119 } else {
2120 int iwidth = cfile->hsize, iheight = cfile->vsize;
2121 calc_maxspect(rwidth, rheight, &iwidth, &iheight);
2122
2123 if (iwidth == cfile->hsize && iheight == cfile->vsize) {
2124 iwidth = -iwidth;
2125 iheight = -iheight;
2126 }
2127
2128 else {
2129 if (LETTERBOX_NEEDS_COMPOSITE && !capable->has_composite) {
2130 do_lb_composite_error();
2131 return -cur_frames;
2132 }
2133
2134 if (LETTERBOX_NEEDS_CONVERT && !capable->has_convert) {
2135 do_lb_convert_error();
2136 return -cur_frames;
2137 }
2138 }
2139 com = lives_strdup_printf("%s reorder \"%s\" \"%s\" %d %d %d %d %d %d %d", prefs->backend, cfile->handle,
2140 get_image_ext_for_type(cfile->img_type), !mainw->endian, rwidth, rheight,
2141 reorder_leave_back, cfile->frames, iwidth, iheight);
2142 }
2143 }
2144
2145 cfile->frames = 0;
2146
2147 cfile->progress_start = 1;
2148 cfile->progress_end = save_event_frames(); // we convert cfile->event_list to a block and save it
2149
2150 if (cfile->progress_end == -1) return -cur_frames; // save_event_frames failed
2151
2152 if (cur_frames > cfile->progress_end) cfile->progress_end = cur_frames;
2153
2154 cfile->next_event = NULL;
2155 if (cfile->event_list) {
2156 if (cfile->event_list_back) event_list_free(cfile->event_list_back);
2157 cfile->event_list_back = cfile->event_list;
2158 cfile->event_list = NULL;
2159 }
2160
2161 lives_rm(cfile->info_file);
2162 mainw->error = FALSE;
2163 lives_system(com, FALSE);
2164 if (THREADVAR(com_failed)) return -cur_frames;
2165
2166 if (cfile->undo_action == UNDO_RESAMPLE) {
2167 if (mainw->current_file > 0) {
2168 cfile->nopreview = cfile->nokeep = TRUE;
2169 if (!do_progress_dialog(TRUE, TRUE, _("Resampling video"))) {
2170 cfile->nopreview = cfile->nokeep = FALSE;
2171 return cur_frames;
2172 }
2173 cfile->nopreview = cfile->nokeep = FALSE;
2174 } else {
2175 do_progress_dialog(TRUE, FALSE, _("Resampling clipboard video"));
2176 }
2177 } else {
2178 cfile->nopreview = cfile->nokeep = TRUE;
2179 if (!do_progress_dialog(TRUE, TRUE, _("Reordering frames"))) {
2180 cfile->nopreview = cfile->nokeep = FALSE;
2181 return cur_frames;
2182 }
2183 cfile->nopreview = cfile->nokeep = FALSE;
2184 }
2185 lives_free(com);
2186
2187 if (mainw->error) {
2188 widget_opts.non_modal = TRUE;
2189 if (mainw->cancelled != CANCEL_ERROR) do_error_dialog(_("\n\nLiVES was unable to reorder the frames."));
2190 widget_opts.non_modal = FALSE;
2191 deorder_frames(new_frames, FALSE);
2192 new_frames = -new_frames;
2193 } else {
2194 array = lives_strsplit(mainw->msg, "|", 2);
2195
2196 new_frames = atoi(array[1]);
2197 lives_strfreev(array);
2198
2199 if (cfile->frames > new_frames) {
2200 new_frames = cfile->frames;
2201 }
2202 }
2203
2204 return new_frames;
2205 }
2206
2207
deorder_frames(int old_frames,boolean leave_bak)2208 int deorder_frames(int old_frames, boolean leave_bak) {
2209 char *com;
2210 ticks_t time_start;
2211 int perf_start, perf_end;
2212
2213 if (cfile->event_list) return cfile->frames;
2214
2215 cfile->event_list = cfile->event_list_back;
2216 cfile->event_list_back = NULL;
2217
2218 if (cfile->event_list == NULL) {
2219 perf_start = 1;
2220 perf_end = old_frames;
2221 } else {
2222 time_start = get_event_timecode(get_first_event(cfile->event_list));
2223 perf_start = (int)(cfile->fps * (double)time_start / TICKS_PER_SECOND_DBL) + 1;
2224 perf_end = perf_start + count_events(cfile->event_list, FALSE, 0, 0) - 1;
2225 }
2226 com = lives_strdup_printf("%s deorder \"%s\" %d %d %d \"%s\" %d", prefs->backend, cfile->handle,
2227 perf_start, cfile->frames, perf_end,
2228 get_image_ext_for_type(cfile->img_type), leave_bak);
2229
2230 lives_rm(cfile->info_file);
2231 lives_system(com, TRUE);
2232 if (THREADVAR(com_failed)) return cfile->frames;
2233
2234 do_progress_dialog(TRUE, FALSE, _("Deordering frames"));
2235 lives_free(com);
2236
2237 // check for EOF
2238
2239 if (cfile->frame_index_back) {
2240 int current_frames = cfile->frames;
2241 cfile->frames = old_frames;
2242 restore_frame_index_back(mainw->current_file);
2243 cfile->frames = current_frames;
2244 }
2245
2246 return old_frames;
2247 }
2248
2249
resample_clipboard(double new_fps)2250 boolean resample_clipboard(double new_fps) {
2251 // resample the clipboard video - if we already did it once, it is
2252 // quicker the second time
2253 char *com;
2254 int current_file = mainw->current_file;
2255
2256 mainw->no_switch_dprint = TRUE;
2257
2258 if (clipboard->undo1_dbl == new_fps && !prefs->conserve_space) {
2259 int new_frames;
2260 double old_fps = clipboard->fps;
2261
2262 if (new_fps == clipboard->fps) {
2263 mainw->no_switch_dprint = FALSE;
2264 return TRUE;
2265 }
2266
2267 // we already resampled to this fps
2268 new_frames = count_resampled_frames(clipboard->frames, clipboard->fps, new_fps);
2269
2270 mainw->current_file = 0;
2271
2272 // copy .mgk to .img_ext and .img_ext to .bak (i.e redo the resample)
2273 com = lives_strdup_printf("%s redo \"%s\" %d %d \"%s\"", prefs->backend, cfile->handle, 1, new_frames,
2274 get_image_ext_for_type(cfile->img_type));
2275 lives_rm(cfile->info_file);
2276 lives_system(com, FALSE);
2277
2278 if (THREADVAR(com_failed)) {
2279 mainw->no_switch_dprint = FALSE;
2280 d_print_failed();
2281 return FALSE;
2282 }
2283
2284 cfile->progress_start = 1;
2285 cfile->progress_end = new_frames;
2286 cfile->old_frames = cfile->frames;
2287 // show a progress dialog, not cancellable
2288 do_progress_dialog(TRUE, FALSE, _("Resampling clipboard video"));
2289 lives_free(com);
2290 cfile->frames = new_frames;
2291 cfile->undo_action = UNDO_RESAMPLE;
2292 cfile->fps = cfile->undo1_dbl;
2293 lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), cfile->fps);
2294 cfile->undo1_dbl = old_fps;
2295 d_print(_("Clipboard was resampled to %d frames.\n"), cfile->frames);
2296 mainw->current_file = current_file;
2297 } else {
2298 if (clipboard->undo1_dbl < clipboard->fps) {
2299 int old_frames = count_resampled_frames(clipboard->frames, clipboard->fps, clipboard->undo1_dbl);
2300 mainw->current_file = 0;
2301 com = lives_strdup_printf("%s undo \"%s\" %d %d \"%s\"", prefs->backend, cfile->handle, old_frames + 1, cfile->frames,
2302 get_image_ext_for_type(cfile->img_type));
2303 lives_rm(cfile->info_file);
2304 lives_system(com, FALSE);
2305 cfile->progress_start = old_frames + 1;
2306 cfile->progress_end = cfile->frames;
2307 // show a progress dialog, not cancellable
2308 do_progress_dialog(TRUE, FALSE, _("Resampling clipboard video"));
2309 lives_free(com);
2310 }
2311
2312 // resample to cfile fps
2313 mainw->current_file = current_file;
2314 clipboard->undo1_dbl = new_fps;
2315
2316 if (new_fps == clipboard->fps) {
2317 mainw->no_switch_dprint = FALSE;
2318 return TRUE;
2319 }
2320
2321 mainw->current_file = 0;
2322 on_resample_vid_ok(NULL, NULL);
2323 mainw->current_file = current_file;
2324 if (clipboard->fps != new_fps) {
2325 d_print(_("resampling error..."));
2326 mainw->error = 1;
2327 mainw->no_switch_dprint = FALSE;
2328 return FALSE;
2329 }
2330 // clipboard->fps now holds new_fps, clipboard->undo1_dbl holds orig fps
2331 // BUT we will later undo this, then clipboard->fps will hold orig fps,
2332 // clipboard->undo1_dbl will hold resampled fps
2333
2334 }
2335
2336 mainw->no_switch_dprint = FALSE;
2337 return TRUE;
2338 }
2339