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