1 /*
2  * Copyright (C) 2002 2003 2004 2005 2007 2008 2010 2011 2012, Magnus Hjorth
3  *
4  * This file is part of mhWaveEdit.
5  *
6  * mhWaveEdit is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * mhWaveEdit is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with mhWaveEdit; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 
22 #include <config.h>
23 
24 #include <pwd.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <locale.h>
31 #include <signal.h>
32 #include <ctype.h>
33 #include <gtk/gtk.h>
34 #include "mainloop.h"
35 #include "mainwindow.h"
36 #include "datasource.h"
37 #include "player.h"
38 #include "chunk.h"
39 #include "sound.h"
40 #include "main.h"
41 #include "inifile.h"
42 #include "um.h"
43 #include "effectbrowser.h"
44 #include "soxdialog.h"
45 #include "statusbar.h"
46 #include "ladspadialog.h"
47 #include "gettext.h"
48 #include "session.h"
49 
50 #ifdef HAVE_SCHED_H
51 #include <sched.h>
52 #endif
53 
54 GdkPixmap *icon = NULL;
55 gboolean quitflag;
56 gboolean quality_mode = TRUE;
57 gchar *driver_option = NULL;
58 
59 int dither_editing;
60 int dither_playback;
61 
strip_context(const char * s)62 const char *strip_context(const char *s)
63 {
64      const char *c;
65      if (s[0] == '|') {
66 	  c = strchr(s+1,'|');
67 	  if (c != NULL) return c+1;
68      }
69      return s;
70 }
71 
idle_work(gpointer csource,gpointer user_data)72 static int idle_work(gpointer csource, gpointer user_data)
73 {
74      if (!idle_work_flag) {
75 	  idle_work_flag = TRUE;
76 	  return 1;
77      }
78      if (status_bar_progress_count() > 0) return 1;
79      if (mainwindow_update_caches()) return 1;
80      return -1;
81 }
82 
83 #if GTK_MAJOR_VERSION == 1
84 
gdk_gc_set_rgb_fg_color(GdkGC * gc,GdkColor * clr)85 void gdk_gc_set_rgb_fg_color(GdkGC *gc, GdkColor *clr)
86 {
87      static GdkColor cached = {0,-1,-1,-1};
88      if (clr->red == cached.red && clr->green == cached.green &&
89 	 clr->blue == cached.blue)
90 	  clr->pixel = cached.pixel;
91      else {
92 	  gdk_colormap_alloc_color(gdk_colormap_get_system(),
93 				   clr,FALSE,TRUE);
94 	  memcpy(&cached,clr,sizeof(cached));
95      }
96      gdk_gc_set_foreground(gc, clr);
97 }
98 
99 #endif
100 
101 #if GTK_MAJOR_VERSION == 1 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 18)
102 
gtk_widget_set_has_window(GtkWidget * w,gboolean has_window)103 void gtk_widget_set_has_window(GtkWidget *w, gboolean has_window)
104 {
105      if (!has_window)
106 	  GTK_WIDGET_SET_FLAGS(w,GTK_NO_WINDOW);
107      else
108 	  GTK_WIDGET_UNSET_FLAGS(w,GTK_NO_WINDOW);
109 }
110 
gtk_widget_set_can_focus(GtkWidget * w,gboolean can_focus)111 void gtk_widget_set_can_focus(GtkWidget *w, gboolean can_focus)
112 {
113      if (!can_focus)
114 	  GTK_WIDGET_SET_FLAGS(w,GTK_CAN_FOCUS);
115      else
116 	  GTK_WIDGET_UNSET_FLAGS(w,GTK_CAN_FOCUS);
117 }
118 
119 #endif
120 
main(int argc,char ** argv)121 int main(int argc, char **argv)
122 {
123      int i;
124      int wavefile_count=0;
125      gboolean ladspa = TRUE;
126      gboolean b;
127      gchar *c;
128 
129      /* Some versions of the aRts C library call g_thread_init internally,
130       * and that confuses GLib2's memory allocation stuff. */
131 #ifdef HAVE_GTHREAD
132      g_thread_init(NULL);
133 #endif
134 
135      /* Set default locale */
136      setlocale(LC_ALL,"");
137 
138      /* This must be the same for all locales */
139      setlocale(LC_NUMERIC, "POSIX");
140 
141     /* Disable gtk's ability to set the locale. */
142     /* If gtk is allowed to set the locale, then it will override the above */
143     /* setlocale for LC_NUMERIC. */
144 #if GTK_MAJOR_VERSION == 2
145      gtk_disable_setlocale();
146 #endif
147 
148 #ifdef ENABLE_NLS
149      /* Setup message domain */
150      bindtextdomain("mhwaveedit", LOCALEDIR);
151      textdomain("mhwaveedit");
152 #if GTK_MAJOR_VERSION == 2
153      bind_textdomain_codeset("mhwaveedit", "UTF-8");
154 #endif
155 #endif
156 
157      floating_point_check();
158 
159 
160      /* Check for "terminating" options that don't require starting GTK. */
161      for (i=1; i<argc; i++) {
162 	  if (!strcmp(argv[i],"--version")) {
163 	       puts(PROGRAM_VERSION_STRING);
164 	       return 0;
165 	  } else if (!strcmp(argv[i],"--help")) {
166                printf(_("Syntax: %s [files]\n"),argv[0]);
167                return 0;
168 	  } else if (!strcmp(argv[i],"--test")) {
169 		puts(_("Testing conversion functions:"));
170 		conversion_selftest();
171 		puts(_("Testing conversion functions finished."));
172 		return 0;
173 	  } else if (!strcmp(argv[i],"--perftest")) {
174 	       conversion_performance_test();
175 	       return 0;
176 	  } else if (!strcmp(argv[i],"--")) break;
177      }
178 
179      /* Setup GTK */
180      gtk_init(&argc,&argv);
181 
182      /* gdk_window_set_debug_updates(TRUE); */
183      /* Check for options. */
184      for (i=1; i<argc; i++) {
185 	  if (!strcmp(argv[i],"--no-ladspa")) {
186 	       ladspa = FALSE;
187 	  } else if (!strcmp(argv[i],"--driver")) {
188 	       i++;
189 	       if (i == argc)
190 		    console_message(_("Expected driver name after "
191 				      "--driver option"));
192 	       else
193 		    driver_option = argv[i];
194 	  } else if (!strcmp(argv[i],"--")) break;
195 	  else if (argv[i][0] == '-') {
196 	       c = g_strdup_printf(_("Unknown option '%s'"),argv[i]);
197 	       console_message(c);
198 	       g_free(c);
199 	       return 1;
200 	  }
201      }
202 
203      /* Call init functions. */
204      inifile_init();
205      sound_init();
206      session_init(&argc,argv);
207 
208      /* Setup some global flags from inifile */
209      status_bar_roll_cursor=inifile_get_gboolean("rollCursor",FALSE);
210      view_follow_strict_flag = inifile_get_gboolean("centerCursor",TRUE);
211      autoplay_mark_flag = inifile_get_gboolean("autoPlayMark",FALSE);
212      varispeed_reset_flag = inifile_get_gboolean("speedReset",FALSE);
213      varispeed_smooth_flag = inifile_get_gboolean("speedSmooth",TRUE);
214      dataformat_get_from_inifile("playerFallback",TRUE,
215 				 &player_fallback_format);
216      chunk_filter_use_floating_tempfiles =
217 	  inifile_get_gboolean("tempfilesFP",TRUE);
218      dither_editing = inifile_get_guint32("ditherEditing",DITHER_MINIMAL);
219      if (dither_editing < 0 || dither_editing > DITHER_MAX)
220 	  dither_editing = DITHER_MINIMAL;
221      dither_playback = inifile_get_gboolean("ditherPlayback",DITHER_NONE);
222      if (dither_playback < 0 || dither_playback > DITHER_MAX)
223 	  dither_playback = DITHER_NONE;
224      sample_convert_mode = inifile_get_guint32("sampleMode",CONVERT_MODE_NOOFFS);
225      if (sample_convert_mode < 0 || sample_convert_mode > CONVERT_MODE_MAX)
226 	  sample_convert_mode = CONVERT_MODE_NOOFFS;
227 
228      gtk_hbutton_box_set_layout_default(GTK_BUTTONBOX_END);
229 
230      mainwindow_objects = list_object_new(FALSE);
231      document_objects = list_object_new(FALSE);
232 
233      default_time_mode = inifile_get_guint32(INI_SETTING_TIME_DISPLAY,0);
234      if (default_time_mode > 6) default_time_mode = 0;
235      default_timescale_mode =
236 	  inifile_get_guint32(INI_SETTING_TIME_DISPLAY_SCALE,1);
237      if (default_timescale_mode > 6) default_time_mode = 0;
238 
239      /* Some color related stuff */
240      set_custom_colors(NULL);
241 
242      /* Register effects */
243      effect_register_init();
244      if (ladspa) ladspa_dialog_register();
245      sox_dialog_register();
246 
247      um_use_gtk = TRUE;
248 
249      /* Search command line for filenames and create windows */
250      b = TRUE;
251      for (i=1;i<argc;i++) {
252 	  if (b && !strcmp(argv[i],"--")) b = FALSE;
253 	  else if (b && !strcmp(argv[i],"--driver")) { i++; continue; }
254 	  else if (b && argv[i][0] == '-') continue;
255 	  gtk_widget_show(mainwindow_new_with_file(argv[i],TRUE));
256 	  wavefile_count++;
257      }
258      if (wavefile_count==0 && !session_dialog())
259 	  gtk_widget_show(mainwindow_new());
260 
261      /* gtk_idle_add(idle_work,NULL); */
262 
263      /* Add low priority idle function */
264      mainloop_constant_source_add(idle_work,NULL,TRUE);
265 
266      /* Run it! */
267      while (!quitflag)
268 	  mainloop();
269 
270 
271 
272      /* Cleanup */
273      player_stop();
274      if (playing_document != NULL) {
275 	  gtk_object_unref(GTK_OBJECT(playing_document));
276 	  playing_document = NULL;
277      }
278      sound_quit();
279      effect_browser_shutdown();
280      inifile_quit();
281      session_quit();
282      g_assert ( chunk_alive_count() == 0 && datasource_count() == 0);
283      return 0;
284 }
285 
286 
287 
namepart(gchar * fullname)288 gchar *namepart(gchar *fullname)
289 {
290      gchar *c;
291      c = strrchr ( fullname, '/' );
292      return c ? c+1 : fullname;
293 }
294 
295 
296 
297 /* timeval_subtract - Stolen from glibc info docs... */
298 
299      /* Subtract the `struct timeval' values X and Y,
300         storing the result in RESULT.
301         Return 1 if the difference is negative, otherwise 0.  */
302 
303 int
timeval_subtract(result,x,y)304      timeval_subtract (result, x, y)
305           GTimeVal *result, *x, *y;
306      {
307        /* Perform the carry for the later subtraction by updating Y. */
308        if (x->tv_usec < y->tv_usec) {
309          int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
310          y->tv_usec -= 1000000 * nsec;
311          y->tv_sec += nsec;
312        }
313        if (x->tv_usec - y->tv_usec > 1000000) {
314          int nsec = (x->tv_usec - y->tv_usec) / 1000000;
315          y->tv_usec += 1000000 * nsec;
316          y->tv_sec -= nsec;
317        }
318 
319        /* Compute the time remaining to wait.
320           `tv_usec' is certainly positive. */
321        result->tv_sec = x->tv_sec - y->tv_sec;
322        result->tv_usec = x->tv_usec - y->tv_usec;
323 
324        /* Return 1 if result is negative. */
325        return x->tv_sec < y->tv_sec;
326      }
327 
328 
get_home_directory(void)329 gchar *get_home_directory(void)
330 {
331      static gchar *homedir = NULL;
332      struct passwd *p;
333      if (!homedir)
334 	  homedir = getenv("HOME");
335      if (!homedir) {
336 	  p = getpwuid(getuid());
337 	  if (p) homedir = p->pw_dir;
338      }
339      if (!homedir) {
340 	  g_warning(_("Could not find home directory. Using current directory as "
341 		    "home directory."));
342 	  homedir = ".";
343      }
344      return homedir;
345 }
346 
347 static gboolean color_alloced[LAST_COLOR] = { 0 };
348 GdkColor color_table[LAST_COLOR];
349 
350 /* old defaults
351 GdkColor factory_default_colors[] = {
352      { 0, 0, 0, 0 }, { 0, 0, 0, 0xFFFF }, { 0, 0xFFFF, 0, 0 },
353      { 0, 0x8000, 0x8000, 0x8000 }, { 0, 0x0f00, 0x8400, 0x6900 },
354      { 0, 0xE800, 0xA500, 0x4000 }, { 0, 0xff00, 0xff00, 0xff00 },
355      { 0, 0x8000, 0x8000, 0x8000 }
356 };
357 */
358 
359 GdkColor factory_default_colors[] = {
360      { 0, 0, 0, 0 }, { 0, 29952, 50176, 27392 }, { 0, 41216, 39936, 52992 },
361      { 0, 62720, 62720, 62720 }, { 0, 60160, 55552, 0 },
362      { 0, 43776, 11776, 0 }, { 0, 0xff00, 0xff00, 0xff00 },
363      { 0, 0x8000, 0x8000, 0x8000 }, { 0, 0x6200, 0x7c00, 0x5e00 }
364 };
365 static GdkGC *gc_table[LAST_COLOR] = { };
366 
367 gchar *color_names[] = {
368     N_("Black"),
369     N_("White"),
370     N_("Background"),
371     N_("L Waveform"),
372     N_("R Waveform"),
373     N_("Cursor"),
374     N_("Marks"),
375     N_("Selection"),
376     N_("Progress bar"),
377     N_("Zero-level"),
378     N_("Buffer position")
379 };
380 
381 gchar *color_inifile_entry[] = { NULL, NULL, "colorBG",
382 				 "colorWave1", "colorWave2",
383 				 "colorCursor","colorMark",
384 				 "colorSelection","colorProgress",
385 				 "colorBars","colorBufpos"
386 };
387 
get_color(enum Color c)388 GdkColor *get_color(enum Color c)
389 {
390      if (!color_alloced[c]) {
391 	  if (c == BLACK)
392 	       gdk_color_black(gdk_colormap_get_system(),&color_table[c]);
393 	  else if (c == WHITE)
394 	       gdk_color_white(gdk_colormap_get_system(),&color_table[c]);
395 	  else {
396 	       gdk_colormap_alloc_color(gdk_colormap_get_system(),
397 					&color_table[c],FALSE,TRUE);
398 	  }
399      }
400      color_alloced[c] = TRUE;
401      return &color_table[c];
402 }
403 
get_gc(enum Color c,GtkWidget * w)404 GdkGC *get_gc(enum Color c, GtkWidget *w)
405 {
406      if (gc_table[c] == NULL) {
407 	  gc_table[c] = gdk_gc_new(w->window);
408 	  gdk_gc_set_foreground(gc_table[c],get_color(c));
409      }
410      return gc_table[c];
411 }
412 
hexval(gchar chr)413 int hexval(gchar chr)
414 {
415      if (chr >= '0' && chr <= '9') return chr-'0';
416      else if (chr >= 'a' && chr <= 'f') return chr-'a'+10;
417      else if (chr >= 'A' && chr <= 'F') return chr-'A'+10;
418      else return -1;
419 }
420 
parse_color(gchar * str,GdkColor * color)421 static void parse_color(gchar *str, GdkColor *color)
422 {
423      unsigned int i,j,k,rgb[3];
424      for (i=0; i<3; i++,str+=2) {
425 	  j = hexval(str[0]);
426 	  if (j == -1) return;
427 	  k = hexval(str[1]);
428 	  if (k == -1) return;
429 	  rgb[i] = j*16+k;
430      }
431      color->red = rgb[0] * 256;
432      color->green = rgb[1] * 256;
433      color->blue = rgb[2] * 256;
434 }
435 
set_custom_colors(GdkColor * c)436 void set_custom_colors(GdkColor *c)
437 {
438      gint i;
439      gchar *d;
440      if (!c) {
441 	  set_custom_colors(factory_default_colors);
442 	  for (i=FIRST_CUSTOM_COLOR; i<LAST_COLOR; i++) {
443 	       d = inifile_get(color_inifile_entry[i],NULL);
444 	       /* printf("%s -> %s\n",color_inifile_entry[i],d); */
445 	       if (d != NULL) {
446 		    /* printf("Before: %x,%x,%x\n",color_table[i].red,
447 		       color_table[i].green,color_table[i].blue); */
448 		    parse_color(d,&color_table[i]);
449 		    /* printf("After: %x,%x,%x\n",color_table[i].red,
450 		       color_table[i].green,color_table[i].blue);  */
451 	       }
452 	  }
453      } else
454 	  memcpy(&color_table[FIRST_CUSTOM_COLOR],c,
455 		 CUSTOM_COLOR_COUNT*sizeof(GdkColor));
456      memset(color_alloced,0,sizeof(color_alloced));
457      for (i=FIRST_CUSTOM_COLOR; i<LAST_COLOR; i++) {
458 	  if (gc_table[i] != NULL) {
459 	       gdk_gc_unref(gc_table[i]);
460 	       gc_table[i] = NULL;
461 	  }
462      }
463      mainwindow_repaint_views();
464 }
465 
save_colors(void)466 void save_colors(void)
467 {
468      int i;
469      gchar triplet[7];
470      for (i=FIRST_CUSTOM_COLOR; i<LAST_COLOR; i++) {
471 	  g_snprintf(triplet,sizeof(triplet),"%.2x%.2x%.2x",
472 		     color_table[i].red/256, color_table[i].green/256,
473 		     color_table[i].blue/256);
474 	  inifile_set(color_inifile_entry[i],triplet);
475      }
476 }
477 
do_yield(gboolean may_sleep)478 void do_yield(gboolean may_sleep)
479 {
480 #ifdef HAVE_SCHED_YIELD
481      static int lasttime=0, yieldcount=0;
482      int i;
483      if (!may_sleep) { sched_yield(); return; }
484      i = time(0);
485      if (i != lasttime) { yieldcount=0; lasttime=i; }
486      sched_yield();
487      yieldcount++;
488      if (yieldcount == 20) { usleep(1000); yieldcount=0; }
489 #else
490      if (may_sleep) usleep(1000);
491 #endif
492 }
493 
launch_mixer(void)494 void launch_mixer(void)
495 {
496      gchar *m,*c;
497      pid_t p;
498      m = inifile_get(INI_SETTING_MIXER,INI_SETTING_MIXER_DEFAULT);
499      p = fork();
500      if (p == -1) {
501 	  c = g_strdup_printf(_("Error launching mixer: fork: %s"),strerror(errno));
502 	  user_error(c);
503 	  g_free(c);
504 	  return;
505      }
506      if (p != 0) return;
507      close_all_files();
508      if (execl("/bin/sh","sh","-c",m,NULL) == -1) {
509 	  fprintf(stderr,"mhwaveedit: execl: %s: %s\n",m,strerror(errno));
510 	  _exit(1);
511      }
512 }
513 
free2(gpointer key,gpointer value,gpointer user_data)514 gboolean free2(gpointer key, gpointer value, gpointer user_data)
515 {
516      g_free(key);
517      g_free(value);
518      return TRUE;
519 }
520 
521 #define BYTESWAP_BSIZE 1*2*3*4*5*6
522 
byteswap(void * buffer,int element_size,int buffer_size)523 void byteswap(void *buffer, int element_size, int buffer_size)
524 {
525      int i,j;
526      char *c,*d,*bufend=NULL;
527      char tempbuf[BYTESWAP_BSIZE];
528      g_assert(element_size < 7 || element_size==8);
529      if (element_size == 1) return;
530      while (buffer_size != 0) {
531 	  i = BYTESWAP_BSIZE;
532 	  if (i > buffer_size) i=buffer_size;
533 	  bufend = ((char *)buffer) + i;
534 	  for (j=0; j<element_size; j++) {
535 	       c = ((char *)buffer)+j;
536 	       d = tempbuf+element_size-1-j;
537 	       while (c<bufend) {
538 		    *d = *c;
539 		    c += element_size;
540 		    d += element_size;
541 	       }
542 	  }
543 	  memcpy(buffer,tempbuf,i);
544 	  buffer = bufend;
545 	  buffer_size -= i;
546      }
547 }
548 
channel_name(guint chan,guint total)549 gchar *channel_name(guint chan, guint total)
550 {
551      static gchar buf[15];
552      if (total == 1) return _("Mono");
553      else if (chan == 0) return _("Left");
554      else if (chan == 1) return _("Right");
555      else {
556 	  g_snprintf(buf,sizeof(buf),_("Ch%d"),chan+1);
557 	  return buf;
558      }
559 }
560 
channel_char(guint chan)561 gchar channel_char(guint chan)
562 {
563      if (chan == 0) return 'L';
564      else if (chan == 1) return 'R';
565      else return '1'+chan;
566 }
567 
channel_format_name(guint chans)568 gchar *channel_format_name(guint chans)
569 {
570      static gchar buf[15];
571      if (chans == 1) return _("Mono");
572      else if (chans == 2) return _("Stereo");
573      else {
574 	  g_snprintf(buf,sizeof(buf),_("%d channels"),chans);
575 	  return buf;
576      }
577 }
578 
attach_label(gchar * text,GtkWidget * table,guint y,guint x)579 GtkLabel *attach_label(gchar *text, GtkWidget *table, guint y, guint x)
580 {
581      GtkWidget *l;
582      l = gtk_label_new(text);
583      gtk_misc_set_alignment(GTK_MISC(l),0.0,0.5);
584      gtk_table_attach(GTK_TABLE(table),l,x,x+1,y,y+1,GTK_FILL,0,0,0);
585      return GTK_LABEL(l);
586 }
587 
get_time(guint32 samplerate,off_t samples,off_t samplemax,gchar * timebuf,gint mode)588 gchar *get_time(guint32 samplerate, off_t samples, off_t samplemax,
589 		gchar *timebuf, gint mode)
590 {
591      static gchar static_buf[64];
592      gfloat secs, ffps;
593      guint mins, msecs, hours, maxhours, frames, ifps, isecs;
594      guint fptm;
595 
596      if (samplemax == 0) samplemax = samples;
597 
598      if (!timebuf)
599 	  timebuf = static_buf;
600 
601      if (mode > 6) mode = 0;
602 
603      if (mode == 2) {
604 	  g_snprintf(timebuf,50,"%05" OFF_T_FORMAT,(OFF_T_FTYPE)samples);
605      } else if (mode < 2) {
606 	  secs = (gfloat) samples / (gfloat) samplerate;
607 	  mins = (guint) (secs / 60.0);
608 	  hours = mins / 60;
609 	  mins = mins % 60;
610 	  msecs = ((guint) (secs  * 1000.0));
611 	  msecs %= 60000;
612 	  maxhours = samplemax / (samplerate * 3600);
613 	  if (mode == 0) {
614 	       if (maxhours > 0)
615 		    g_snprintf(timebuf,50,"%d'%02d:%02d.%d",hours,mins,
616 			       msecs/1000,(msecs%1000)/100);
617 	       else
618 		    g_snprintf(timebuf,50,"%02d:%02d.%d",mins,
619 			       msecs/1000,(msecs%1000)/100);
620 	  } else if (mode == 1) {
621 	       if (maxhours > 0)
622 		    g_snprintf(timebuf,50,"%d'%02d:%02d.%03d",hours,mins,
623 			       msecs/1000,msecs%1000);
624 	       else
625 		    g_snprintf(timebuf,50,"%02d:%02d.%03d",mins,
626 			       msecs/1000,msecs%1000);
627 	  } else {
628 	       if (maxhours > 0)
629 		    g_snprintf(timebuf,50,"%d'%02d:%02d",hours,mins,
630 			       msecs/1000);
631 	       else
632 		    g_snprintf(timebuf,50,"%02d:%02d",mins,msecs/1000);
633 	  }
634      } else {
635 	  secs = (gfloat) samples / (gfloat) samplerate;
636 
637 	  if (mode == 3) { ffps=24.0; ifps=24; }
638 	  else if (mode == 4) { ffps=25.0; ifps=25; }
639 	  else if (mode == 5) { ffps=30.0*1.000/1.001; ifps=30; }
640 	  else { ffps=30.0; ifps=30; }
641 
642 	  frames = (guint) (secs * ffps);
643 
644 	  /* Ten-minute units never need to be dropped. */
645 	  if (mode == 5) {
646 	       fptm = 60*30*10 - 2*9;
647 	  } else {
648 	       fptm = ifps * 600;
649 	  }
650 	  mins = 10 * (frames / fptm);
651 	  frames = frames % fptm;
652 
653 	  /* Translate the remaining frames to mins+isecs+frames */
654 	  if (mode != 5) {
655 	       isecs = frames / ifps;
656 	       frames %= ifps;
657 	       mins += isecs / 60;
658 	       isecs %= 60;
659 	  } else {
660 	       /* First minute is 60x30 frames */
661 	       if (frames >= 60*30) {
662 		    mins ++;
663 		    frames -= 60*30;
664 		    /* Remaining minutes are 60x30-2 frames */
665 		    mins += frames / (60*30-2);
666 		    frames %= (60*30-2);
667 		    /* Skip two frames on the last minute */
668 		    frames += 2;
669 	       }
670 	       isecs = frames / ifps;
671 	       frames %= ifps;
672 	       g_assert(isecs < 60);
673 	  }
674 
675 	  hours = mins / 60;
676 	  mins = mins % 60;
677 
678 	  g_snprintf(timebuf,50,"%02d:%02d:%02d[%02d]", hours, mins, isecs,
679 		     frames);
680      }
681      return timebuf;
682 }
683 
684 guint default_time_mode = 100, default_timescale_mode = 100;
685 
get_time_s(guint32 samplerate,off_t samples,off_t samplemax,gchar * timebuf)686 gchar *get_time_s(guint32 samplerate, off_t samples, off_t samplemax,
687 		  gchar *timebuf)
688 {
689      return get_time(samplerate,samples,samplemax,timebuf,-1);
690 }
691 
get_time_l(guint32 samplerate,off_t samples,off_t samplemax,gchar * timebuf)692 gchar *get_time_l(guint32 samplerate, off_t samples, off_t samplemax,
693 		  gchar *timebuf)
694 {
695      return get_time(samplerate,samples,samplemax,timebuf,1);
696 }
697 
get_time_head(guint32 samplerate,off_t samples,off_t samplemax,gchar * timebuf,gint timemode)698 gchar *get_time_head(guint32 samplerate, off_t samples, off_t samplemax,
699 		     gchar *timebuf, gint timemode)
700 {
701      switch (timemode) {
702      case TIMEMODE_REAL:
703      case TIMEMODE_REALLONG:
704      case TIMEMODE_24FPS:
705      case TIMEMODE_25FPS:
706      case TIMEMODE_NTSC:
707      case TIMEMODE_30FPS:
708      default:
709 	  return get_time(samplerate,samples,samplemax,timebuf,-1);
710      case TIMEMODE_SAMPLES:
711 	  return get_time(samplerate,samples,samplemax,timebuf,
712 			    TIMEMODE_SAMPLES);
713      }
714 }
715 
get_time_tail(guint32 samplerate,off_t samples,off_t samplemax,gchar * timebuf,gint timemode)716 gchar *get_time_tail(guint32 samplerate, off_t samples, off_t samplemax,
717 		     gchar *timebuf, gint timemode)
718 {
719 
720      off_t frameno;
721      guint i;
722      switch (timemode) {
723      case TIMEMODE_REAL:
724 	  return NULL;
725      case TIMEMODE_REALLONG:
726 	  i = ((samples % samplerate) * 1000) / samplerate;
727 	  g_snprintf(timebuf,50,".%03d",i);
728 	  return timebuf;
729      case TIMEMODE_24FPS:
730 	  i = ((samples % samplerate) * 24) / samplerate;
731 	  g_snprintf(timebuf,50,"[%02d]",i);
732 	  return timebuf;
733      case TIMEMODE_25FPS:
734 	  i = ((samples % samplerate) * 25) / samplerate;
735 	  g_snprintf(timebuf,50,"[%02d]",i);
736 	  return timebuf;
737      case TIMEMODE_NTSC:
738 	  frameno = (samples * 30 * 1000) / (samplerate * 1001);
739 	  /* Remove all 10 minute blocks of 60x30x10-2x9 frames */
740 	  i = (guint) (frameno % (60*30*10-2*9));
741 	  /* Remove the first minute */
742 	  if (i >= 60*30) {
743 	       i -= 60*30;
744 	       /* Remove subsequent minutes */
745 	       i %= 60*30-2;
746 	       /* Add drop frame in current minute to count */
747 	       i += 2;
748 	  }
749 	  /* Remove seconds */
750 	  i %= 30;
751 	  g_snprintf(timebuf,50,"[%02d]",i);
752 	  return timebuf;
753      case TIMEMODE_30FPS:
754 	  i = ((samples % samplerate) * 30) / samplerate;
755 	  g_snprintf(timebuf,50,"[%02d]",i);
756 	  return timebuf;
757      default:
758      case TIMEMODE_SAMPLES:
759 	  return NULL;
760      }
761 }
762 
763 /* This table specify at which intervals big or small lines can be
764  * drawn. */
765 static const gint bigsizes[] = { 1, 2, 5, 10, 20, 30,
766 				 60, 120, 180, 300, 600,
767 				 900, 1800, 3600, 36000 };
768 /* This table is TRUE whenever the entry in the table above is
769  * not an even divisor in the  entry that follows it. */
770 static const gboolean bigskip[] = { FALSE, TRUE, FALSE, FALSE, TRUE,
771 				    FALSE, FALSE, TRUE, TRUE, FALSE, TRUE,
772 				    TRUE, FALSE, FALSE };
773 
774 static const gint smallsizes_real[] = { 1000, 100, 10 };
775 static const gint smallsizes_24fps[] = { 24, 12, 4 };
776 static const gint smallsizes_25fps[] = { 25, 5 };
777 static const gint smallsizes_30fps[] = { 30, 10, 5 };
778 
779 /* Returns:
780  * 0 - Both major and minor points should have text from the get_time_head
781  *     function.
782  * 1 - Major points should have text from the get_time_head function, and
783  *     minor points should have text from teh get_time_tail function.
784  */
785 
786 /* Midpoints are generated when there is a minor point scale that has
787  * <= nmidpoints elements, is larger than the scale used for the minor points
788  * and the major points are at the smallest scale.
789  * Sounds very messy, but it's for making sure that we always have points
790  * we can draw text on.
791  * */
find_timescale_points(guint32 samplerate,off_t start_samp,off_t end_samp,off_t * points,int * npoints,off_t * midpoints,int * nmidpoints,off_t * minor_points,int * nminorpoints,int timemode)792 guint find_timescale_points(guint32 samplerate, off_t start_samp,
793 			    off_t end_samp,
794 			    off_t *points, int *npoints,
795 			    off_t *midpoints, int *nmidpoints,
796 			    off_t *minor_points, int *nminorpoints,
797 			    int timemode)
798 {
799      guint pctr=0,mpctr=0,midpctr=0;
800      off_t p,q,r,s;
801      int i;
802      const int *ss;
803      int ssl;
804      int max_points = *npoints, max_minorpoints = *nminorpoints;
805      int max_midpoints = *nmidpoints;
806 
807      g_assert(max_points > 2);
808      g_assert(max_minorpoints >= max_points);
809 
810      *nmidpoints = 0;
811 
812      /* Handle sample-based time */
813      if (timemode == TIMEMODE_SAMPLES) {
814 
815 	  p = 1;
816 	  q = start_samp;
817 	  r = end_samp;
818 	  while (r-q >= (off_t)(max_points-1)) {
819 	       q /= 10;
820 	       r /= 10;
821 	       p *= 10;
822 	  }
823 	  for (s=q; s<=r+1; s++) {
824 	       points[pctr++] = s * p;
825 	       g_assert(pctr <= max_points);
826 	  }
827 
828 	  p = 1;
829 	  q = start_samp;
830 	  r = end_samp;
831 	  while (r-q >= (off_t)(max_minorpoints-1)) {
832 	       q /= 10;
833 	       r /= 10;
834 	       p *= 10;
835 	  }
836 	  for (s=q; s<=r+1; s++) {
837 	       minor_points[mpctr++] = s * p;
838 	       g_assert(mpctr <= max_minorpoints);
839 	  }
840 	  *npoints = pctr;
841 	  *nminorpoints = mpctr;
842 	  return 0;
843      }
844 
845      /* Handle major points (common between timecode and real time) */
846      i = 0;
847      while (i < (ARRAY_LENGTH(bigsizes)-1) &&
848 	    ( (end_samp-start_samp)/(bigsizes[i]*samplerate) >
849 	      (off_t)(max_points-2) ) )
850 	  i++;
851      q = start_samp / (bigsizes[i] * samplerate);
852      r = end_samp / (bigsizes[i] * samplerate);
853      /* printf("q = %d, r = %d\n",(int)q,(int)r); */
854      while (1) {
855 	  points[pctr++] = (q++) * bigsizes[i] * samplerate;
856 	  /* printf("q = %d, pctr = %d, max_points = %d\n",(int)q,(int)pctr,(int)max_points); */
857 	  g_assert(pctr <= max_points);
858 	  if (q > r) break;
859      }
860      *npoints = pctr;
861 
862      /* Handle minor points of >=1s */
863      if (i > 0) {
864 	  i--;
865 	  while (bigskip[i]) i--;
866 	  if ( (end_samp-start_samp) / (bigsizes[i]*samplerate) >=
867 	       (off_t)(max_minorpoints-2)) {
868 	       *nminorpoints = 0;
869 	       return 0;
870 	  }
871 	  while (i > 0 &&
872 		 ( (end_samp-start_samp)/(bigsizes[i-1]*samplerate) <
873 		   (off_t)(max_minorpoints-2) ) )
874 	       i--;
875 
876 	  q = start_samp / (bigsizes[i] * samplerate);
877 	  r = end_samp / (bigsizes[i] * samplerate);
878 	  for (s=q; s<=r+1; s++) {
879 	       minor_points[mpctr++] = s * bigsizes[i] * samplerate;
880 	       g_assert(mpctr <= max_minorpoints);
881 	  }
882 	  *nminorpoints = mpctr;
883 	  return 0;
884      }
885 
886      /* Handle sub-second minor ticks for NTSC timecode */
887      /* This is a special case since NTSC frames are not second-aligned */
888      if (timemode == TIMEMODE_NTSC) {
889 	  /* Calculate start and end frame count (rounded down) */
890 	  q = (start_samp * 30 * 1000) / (samplerate * 1001);
891 	  r = (end_samp * 30 * 1000) / (samplerate * 1001);
892 
893 	  if (r-q >= max_minorpoints) {
894 	       *nminorpoints = 0;
895 	       return 0;
896 	  }
897 
898 	  for (s=q; s<=r; s++) {
899 	       /* Convert from frame to sample,  rounding upwards */
900 	       minor_points[mpctr++] =
901 		    (s * samplerate * 1001 + 29999) / (30000);
902 	  }
903 	  *nminorpoints = mpctr;
904 	  return 1;
905      }
906 
907      /* Handle sub-second minor ticks, real time or non-NTSC timecode */
908      switch (timemode) {
909      case TIMEMODE_REAL:
910      case TIMEMODE_REALLONG:
911 	  ss = smallsizes_real;
912 	  ssl = ARRAY_LENGTH(smallsizes_real);
913 	  break;
914      case TIMEMODE_24FPS:
915 	  ss = smallsizes_24fps;
916 	  ssl = ARRAY_LENGTH(smallsizes_24fps);
917 	  break;
918      case TIMEMODE_25FPS:
919 	  ss = smallsizes_25fps;
920 	  ssl = ARRAY_LENGTH(smallsizes_25fps);
921 	  break;
922      case TIMEMODE_30FPS:
923 	  ss = smallsizes_30fps;
924 	  ssl = ARRAY_LENGTH(smallsizes_30fps);
925 	  break;
926      default:
927 	  g_assert_not_reached();
928      }
929 
930      i = 0;
931      while (i < ssl &&
932 	    ((end_samp-start_samp)*ss[i])/samplerate >=
933 	    (off_t)(max_minorpoints-2))
934 	  i++;
935 
936      if (i >= ssl) {
937 	  *nminorpoints = 0;
938 	  return 0;
939      }
940 
941      q = (start_samp*ss[i])/samplerate;
942      r = (end_samp*ss[i])/samplerate;
943 
944      for (s=q; s<=r+1; s++) {
945 	  minor_points[mpctr++] = (s * samplerate + ss[i]-1) / ss[i];
946      }
947      *nminorpoints = mpctr;
948 
949      /* Generate mid points */
950      do {
951 	  i++;
952      } while (i < ssl &&
953 	      ((end_samp-start_samp)*ss[i])/samplerate >=
954 	      (off_t)(max_midpoints-2));
955 
956      if (i >= ssl) return 1;
957 
958      q = (start_samp*ss[i])/samplerate;
959      r = (end_samp*ss[i])/samplerate;
960 
961      for (s=q; s<=r+1; s++) {
962 	  midpoints[midpctr++] = (s * samplerate + ss[i]-1) / ss[i];
963      }
964      *nmidpoints = midpctr;
965 
966      return 1;
967 }
968 
parse_time(gchar * timestr)969 gfloat parse_time(gchar *timestr)
970 {
971      gchar *c,*d;
972      gfloat t = 0.0, f;
973      long int l;
974      int i;
975 
976      c = timestr;
977      /* Hours */
978      d = strchr(c,'\'');
979      if (d != NULL) {
980 	  l = strtol(c,&d,10);
981 	  if (l < 0 || *d != '\'') return -1.0;
982 	  t += (gfloat)l * 3600.0;
983 	  c = d+1;
984      }
985      /* Minutes */
986      d = strchr(c,':');
987      if (d != NULL) {
988 	  l = strtol(c,&d,10);
989 	  if (l < 0 || *d != ':') return -1.0;
990 	  t += (gfloat)l * 60.0;
991 	  c = d+1;
992      }
993      /* Seconds */
994      if (*c != '.') {
995 	  l = strtol(c,&d,10);
996 	  if (l < 0 || (*d != '.' && *d != 0)) return -1.0;
997 	  t += (gfloat)l;
998 	  if (*d == 0) return t;
999 	  c = d + 1;
1000      } else
1001 	  c = c + 1;
1002      /* Millis */
1003      l = strtol(c,&d,10);
1004      if (l < 0 || *d != 0) return -1.0;
1005      f = (gfloat)l;
1006      for (i=0; i<(d-c); i++) f *= 0.1;
1007      t += f;
1008      return t;
1009 }
1010 
1011 struct lookup_keys_priv {
1012      gconstpointer value;
1013      GSList *keys;
1014 };
1015 
lookup_keys_func(gpointer key,gpointer value,gpointer user_data)1016 static void lookup_keys_func(gpointer key, gpointer value,
1017 			     gpointer user_data)
1018 {
1019      struct lookup_keys_priv *p = (struct lookup_keys_priv *)user_data;
1020      if (!strcmp(value,p->value)) p->keys=g_slist_prepend(p->keys,key);
1021 }
1022 
hash_table_lookup_keys(GHashTable * hash_table,gconstpointer value)1023 GSList *hash_table_lookup_keys(GHashTable *hash_table, gconstpointer value)
1024 {
1025      struct lookup_keys_priv p = { value, NULL };
1026      g_hash_table_foreach(hash_table,lookup_keys_func,&p);
1027      return p.keys;
1028 }
1029 
1030 struct geometry_stack_item {
1031      gint x,y;
1032      gint width, height;
1033      gchar *extra;
1034 };
1035 
parse_geom_x(gchar * str,struct geometry_stack_item * result)1036 static gboolean parse_geom_x(gchar *str, struct geometry_stack_item *result)
1037 {
1038      struct geometry_stack_item a;
1039      gchar *d,*e;
1040      a.x = strtoul(str,&d,10);
1041      if (*d != '_') return TRUE;
1042      a.y = strtoul(d+1,&d,10);
1043      if (*d != '_') return TRUE;
1044      a.width = strtoul(d+1,&d,10);
1045      if (*d != '_') return TRUE;
1046      a.height = strtoul(d+1,&e,10);
1047      if (e == d+1) return TRUE;
1048 
1049      if (*e == '_') a.extra = g_strdup(e+1);
1050      else a.extra = NULL;
1051 
1052      memcpy(result,&a,sizeof(*result));
1053 
1054      return FALSE;
1055 }
1056 
parse_geom(gchar * str,GtkAllocation * result)1057 gboolean parse_geom(gchar *str, GtkAllocation *result)
1058 {
1059      struct geometry_stack_item item;
1060      if (parse_geom_x(str,&item)) return TRUE;
1061      if (item.extra) g_free(item.extra);
1062      result->x = item.x;
1063      result->y = item.y;
1064      result->width = item.width;
1065      result->height = item.height;
1066      return FALSE;
1067 }
1068 
1069 
get_geom_x(GtkWindow * window,gchar * extra)1070 static gchar *get_geom_x(GtkWindow *window, gchar *extra)
1071 {
1072      gint x,y,width,height;
1073      gdk_window_get_size(GTK_WIDGET(window)->window,&width,&height);
1074      gdk_window_get_root_origin(GTK_WIDGET(window)->window,&x,&y);
1075      if (extra == NULL)
1076 	  return g_strdup_printf("%d_%d_%d_%d",x,y,width,height);
1077      else
1078 	  return g_strdup_printf("%d_%d_%d_%d_%s",x,y,width,height,extra);
1079 }
1080 
get_geom(GtkWindow * window)1081 gchar *get_geom(GtkWindow *window)
1082 {
1083      return get_geom_x(window,NULL);
1084 }
1085 
geometry_stack_from_inifile(gchar * ininame)1086 GSList *geometry_stack_from_inifile(gchar *ininame)
1087 {
1088      gchar *c,*d;
1089      struct geometry_stack_item a,*ap;
1090      GSList *l = NULL;
1091      gboolean boo;
1092 
1093      c = inifile_get(ininame,NULL);
1094      if (c != NULL) {
1095 	  while (g_slist_length(l) < 64) {
1096 	       d = strchr(c,'|');
1097 	       if (d) *d=0;
1098 	       boo = parse_geom_x(c,&a);
1099 	       if (d) *d='|';
1100 	       if (boo) break;
1101 	       if (a.width > 0 && a.height > 0) {
1102 		    ap = g_malloc(sizeof(*ap));
1103 		    memcpy(ap,&a,sizeof(*ap));
1104 		    l = g_slist_append(l,ap);
1105 	       }
1106 	       if (d == NULL) break;
1107 	       c = d+1;
1108 	  }
1109      }
1110 
1111      return l;
1112 }
1113 
stack_proc(gpointer data,gpointer user_data)1114 static void stack_proc(gpointer data, gpointer user_data)
1115 {
1116      struct geometry_stack_item *all = (struct geometry_stack_item *)data;
1117      gchar **c = (gchar **)user_data;
1118      gchar *d;
1119      if (all->extra)
1120 	  d = g_strdup_printf("%s%s%d_%d_%d_%d_%s",(*c)?(*c):"",(*c)?"|":"",
1121 			      all->x,all->y,all->width,all->height,all->extra);
1122      else
1123 	  d = g_strdup_printf("%s%s%d_%d_%d_%d",(*c)?(*c):"",(*c)?"|":"",
1124 			      all->x,all->y,all->width,all->height);
1125      g_free(*c);
1126      *c = d;
1127 }
1128 
geometry_stack_save_to_inifile(gchar * ininame,GSList * stack)1129 void geometry_stack_save_to_inifile(gchar *ininame, GSList *stack)
1130 {
1131      gchar *c=NULL;
1132      g_slist_foreach(stack,stack_proc,&c);
1133      if (c != NULL)
1134 	  inifile_set(ininame,c);
1135      g_free(c);
1136 }
1137 
geometry_stack_pop(GSList ** stackp,gchar ** extra,GtkWindow * wnd)1138 gboolean geometry_stack_pop(GSList **stackp, gchar **extra, GtkWindow *wnd)
1139 {
1140      struct geometry_stack_item *ap;
1141 
1142      if (*stackp == NULL) return FALSE;
1143 
1144      ap = (struct geometry_stack_item *)((*stackp)->data);
1145      *stackp = g_slist_remove(*stackp,ap);
1146 
1147      if (ap->x < -4000 || ap->x > 4000 || ap->y < -4000 || ap->y > 4000 ||
1148 	 ap->width > 4000 || ap->height > 4000) {
1149 	  fputs(_("Ignoring extreme old window size/position values\n"),stderr);
1150 	  g_free(ap->extra);
1151 	  g_free(ap);
1152 	  return geometry_stack_pop(stackp,extra,wnd);
1153      }
1154      gtk_window_set_default_size(GTK_WINDOW(wnd),ap->width,ap->height);
1155      gtk_widget_set_uposition(GTK_WIDGET(wnd),ap->x,ap->y);
1156      if (extra) *extra = ap->extra;
1157      g_free(ap);
1158 
1159      return TRUE;
1160 }
1161 
geometry_stack_push(GtkWindow * w,gchar * extra,GSList ** stackp)1162 void geometry_stack_push(GtkWindow *w, gchar *extra, GSList **stackp)
1163 {
1164      struct geometry_stack_item *all;
1165      gint x,y,width,height;
1166      all = g_malloc(sizeof(*all));
1167      gdk_window_get_size(GTK_WIDGET(w)->window,&width,&height);
1168      gdk_window_get_root_origin(GTK_WIDGET(w)->window,&x,&y);
1169      all->x = x;
1170      all->y = y;
1171      all->width = width;
1172      all->height = height;
1173      if (extra) all->extra = g_strdup(extra);
1174      else all->extra = NULL;
1175      *stackp = g_slist_prepend(*stackp,all);
1176 }
1177 
translate_strip(const char * s)1178 char *translate_strip(const char *s)
1179 {
1180      char *c;
1181      c = _(s);
1182      if (c == s) {
1183 	  c = strchr(s,'|');
1184 	  g_assert(c != NULL);
1185 	  return c+1;
1186      } else
1187 	  return c;
1188 }
1189 
format_float(float f,char * r,int maxsz)1190 void format_float(float f, char *r, int maxsz)
1191 {
1192      gchar *c;
1193      g_snprintf(r,maxsz,"%#.7g",f);
1194      c = strchr(r,0);
1195      c-=2;
1196      while (c>=r && isdigit(c[0]) && c[1]=='0') {
1197 	  c[1] = 0;
1198 	  c--;
1199      }
1200 }
1201