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