1 /*
2 * Copyright (C) 2004 2005 2008 2009, 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 #include <config.h>
22
23 #include <math.h>
24 #include <gtk/gtk.h>
25 #include <gdk/gdkkeysyms.h>
26
27 #include "recorddialog.h"
28 #include "main.h"
29 #include "inifile.h"
30 #include "sound.h"
31 #include "um.h"
32 #include "int_box.h"
33 #include "formatselector.h"
34 #include "gettext.h"
35 #include "mainloop.h"
36
37 static struct {
38 GtkWindow *wnd;
39 FormatSelector *fs;
40 GtkEntry *name_entry;
41 int old_choice;
42 GtkList *preset_list;
43 GtkLabel *set_button_label;
44 GtkButton *set_button_button;
45 } other_dialog;
46
47 static gboolean record_dialog_set_format(RecordDialog *rd);
48
49 static GtkObjectClass *parent_class;
50 static gboolean record_dialog_stopflag = FALSE;
51 static RecordDialog *current_dialog;
52
53 static ListObject *preset_list = NULL;
54
build_preset_list(void)55 static void build_preset_list(void)
56 {
57 gchar *s1,*s2,*s3,*s4,*s5,*s6;
58 GList *l2 = NULL;
59 int i;
60 RecordFormat *rf;
61
62 if (preset_list != NULL) return;
63
64 /* Build format list */
65 /* Support reading both old and new style formats */
66 if (inifile_get("recordFormat1",NULL) == NULL &&
67 inifile_get("recordFormat1_Name",NULL) == NULL) {
68 inifile_set("recordFormat1","16/2/44100/S/CD Quality");
69 inifile_set("recordFormat2","8/1/8000//Low quality");
70 }
71 for (i=1; ; i++) {
72 s1 = g_strdup_printf("recordFormat%d",i);
73 s2 = inifile_get(s1,NULL);
74 g_free(s1);
75 if (s2 == NULL) {
76 s1 = g_strdup_printf("recordFormat%d_Name",i);
77 s2 = inifile_get(s1,NULL);
78 g_free(s1);
79 if (s2 == NULL) break;
80 rf = g_malloc(sizeof(*rf));
81 s3 = g_strdup_printf("recordFormat%d",i);
82 if (!dataformat_get_from_inifile(s3,TRUE,&(rf->fmt))) {
83 g_free(s3);
84 g_free(rf);
85 continue;
86 }
87 g_free(s3);
88 rf->num = i;
89 rf->name = g_strdup(s2);
90 l2 = g_list_append(l2,rf);
91 } else {
92 s3 = strchr(s2,'/');
93 if (s3 == NULL) continue;
94 s3 += 1;
95 s4 = strchr(s3,'/');
96 if (s4 == NULL) continue;
97 s4 += 1;
98 s5 = strchr(s4,'/');
99 if (s5 == NULL) continue;
100 s5 += 1;
101 s6 = strchr(s5,'/');
102 if (s6 == NULL) continue;
103 s6 += 1;
104 rf = g_malloc(sizeof(*rf));
105 rf->num = i;
106 rf->fmt.type = DATAFORMAT_PCM;
107 rf->fmt.samplesize = ((guint)strtod(s2,NULL)) / 8;
108 rf->fmt.channels = ((guint)strtod(s3,NULL));
109 rf->fmt.samplebytes = rf->fmt.samplesize * rf->fmt.channels;
110 rf->fmt.samplerate = ((guint)strtod(s4,NULL));
111 rf->fmt.sign = FALSE;
112 rf->fmt.bigendian = IS_BIGENDIAN;
113 rf->fmt.packing = 0;
114 for (; *s5 != 0; s5++) {
115 if (*s5 == 'B') rf->fmt.bigendian = !IS_BIGENDIAN;
116 else if (*s5 == 'S') rf->fmt.sign = TRUE;
117 }
118 if (rf->fmt.samplesize < 1 || rf->fmt.samplesize > 4 ||
119 rf->fmt.channels < 1 || rf->fmt.channels > 8 ||
120 rf->fmt.samplerate < 1) {
121 g_free(rf);
122 continue;
123 }
124 rf->name = g_strdup(s6);
125 l2 = g_list_append(l2,rf);
126 }
127 }
128
129 preset_list = list_object_new_from_list(l2,FALSE);
130 gtk_object_ref(GTK_OBJECT(preset_list));
131 }
132
set_preset(gchar * name,Dataformat * fmt)133 static void set_preset(gchar *name, Dataformat *fmt)
134 {
135 RecordFormat *rf;
136 int i;
137 gchar *c,*d;
138 GList *l;
139
140 g_assert(name != NULL);
141
142 /* If there is an old format with the same name, update it.
143 * Otherwise, set the number to one higher than the
144 * currently highest used number. */
145 i = 0;
146 l = preset_list->list;
147 while (l != NULL) {
148 rf = (RecordFormat *)(l->data);
149 if (!strcmp(rf->name,name)) {
150 if (dataformat_equal(&(rf->fmt),fmt)) return;
151 memcpy(&(rf->fmt),fmt,sizeof(Dataformat));
152 break;;
153 }
154 if (rf->num > i) i = rf->num;
155 l = l->next;
156 }
157
158 if (l == NULL) {
159 rf = g_malloc(sizeof(*rf));
160 rf->name = g_strdup(name);
161 rf->fmt = *fmt;
162 rf->num = i+1;
163 list_object_add(preset_list, rf);
164 }
165
166 c = g_strdup_printf("recordFormat%d",rf->num);
167 d = g_strdup_printf("%s_Name",c);
168 inifile_set(d,rf->name);
169 dataformat_save_to_inifile(c,fmt,TRUE);
170 g_free(d);
171 g_free(c);
172
173 if (l != NULL)
174 list_object_notify(preset_list,rf);
175 }
176
compare_preset(gchar * name,Dataformat * fmt)177 static gboolean compare_preset(gchar *name, Dataformat *fmt)
178 {
179 GList *l;
180 RecordFormat *rf;
181 l = preset_list->list;
182 while (l != NULL) {
183 rf = (RecordFormat *)(l->data);
184 if (!strcmp(rf->name,name))
185 return dataformat_equal(&(rf->fmt),fmt);
186 l = l->next;
187 }
188 return FALSE;
189 }
190
reset_peaks(GtkButton * button,gpointer user_data)191 static void reset_peaks(GtkButton *button, gpointer user_data)
192 {
193 int i;
194 RecordDialog *rd = RECORD_DIALOG(user_data);
195 for (i=0; i<8; i++) rd->maxpeak_values[i] = 0.0;
196 }
197
set_limit_label(RecordDialog * rd)198 static void set_limit_label(RecordDialog *rd)
199 {
200 gchar buf[64];
201 if (rd->limit_record) {
202 get_time_s(rd->current_format->samplerate,
203 (rd->limit_bytes-rd->written_bytes) /
204 rd->current_format->samplebytes, 0, buf);
205 gtk_label_set_text(rd->limit_label,buf);
206 } else
207 gtk_label_set_text(rd->limit_label,_("(no limit)"));
208 }
209
process_input(RecordDialog * rd)210 static gboolean process_input(RecordDialog *rd)
211 {
212 guint32 x,y;
213 gchar buf[4096];
214 guint c,d,e;
215 sample_t peak,rms,avg,*sp,*sq,s,pmax;
216 guint32 clip_amount,clip_size;
217 int i;
218 gboolean finish_mode = record_dialog_stopflag;
219
220 if (rd->current_format == NULL) return FALSE;
221 /* Read input */
222 input_store(rd->databuf);
223
224 x = ringbuf_available(rd->databuf);
225 /* Round to even samples */
226 x -= x % rd->current_format->samplebytes;
227 /* If we have <0.1 s of data, wait for more. */
228 if ((x < rd->analysis_bytes && !finish_mode) || x==0) return FALSE;
229 /* Send data older than 0.1s straight into the tempfile if we have one */
230 if (!finish_mode)
231 x -= rd->analysis_bytes;
232 while (x > 0) {
233 y = ringbuf_dequeue(rd->databuf,buf,MIN(x,sizeof(buf)));
234 if (rd->tf != NULL && !rd->paused) {
235 if (tempfile_write(rd->tf,buf,y)) {
236 record_dialog_stopflag = TRUE;
237 return FALSE;
238 }
239 rd->written_bytes += y;
240 if ( (rd->limit_record) &&
241 (rd->written_bytes >= rd->limit_bytes) ) {
242 record_dialog_stopflag = TRUE;
243 return TRUE;
244 }
245 }
246 x -= y;
247 }
248 if (finish_mode) return TRUE;
249 /* Analyse data */
250 y = ringbuf_dequeue(rd->databuf,rd->analysis_buf,rd->analysis_bytes);
251 g_assert(y == rd->analysis_bytes);
252 /* First write out the raw data to disk */
253 if (rd->tf != NULL && !rd->paused) {
254 if (tempfile_write(rd->tf,rd->analysis_buf,y)) {
255 record_dialog_stopflag = TRUE;
256 return FALSE;
257 }
258 rd->written_bytes += y;
259 if ( (rd->limit_record) && (rd->written_bytes >= rd->limit_bytes) )
260 record_dialog_stopflag = TRUE;
261 }
262 convert_array(rd->analysis_buf,rd->current_format,
263 rd->analysis_sbuf,&dataformat_sample_t,
264 rd->analysis_samples, DITHER_NONE, NULL);
265 /* Calculate statistics */
266 sq = &(rd->analysis_sbuf[rd->analysis_samples]);
267 d = rd->current_format->channels;
268 for (c=0; c<d; c++) {
269 peak = rms = avg = 0.0;
270 clip_size = clip_amount = 0;
271 sp = &(rd->analysis_sbuf[c]);
272 for (; sp<sq; sp+=d) {
273 s = *sp;
274 avg += s;
275 s = fabs(s);
276 if (s > peak) peak = s;
277 rms += s*s;
278 *sp = s; /* Save the abs value for clipping calculation */
279 }
280 avg /= ((sample_t)rd->analysis_samples);
281 rms = sqrt((rms)/ ((sample_t)rd->analysis_samples) - avg*avg);
282 pmax = maximum_float_value(&(rd->current_format->fmt));
283 /* Since the conversion routines were changed for 1.2.9, this
284 * algo can give false alarms, but only when you're _very_ near
285 * clipping. */
286 if (peak >= pmax) {
287 /* Calculate clipping amount and size */
288 sp = &(rd->analysis_sbuf[c]);
289 while (1) {
290 for (; sp<sq; sp+=d)
291 if (*sp >= pmax) break;
292 if (sp >= sq) break;
293 for (e=0; sp<sq && *sp>=pmax; e++,sp+=d) { }
294 clip_amount ++;
295 clip_size += e*e;
296 }
297 }
298 /* Set the labels and VU meters */
299 vu_meter_set_value(rd->meters[c],(float)peak);
300 g_snprintf(buf,sizeof(buf),"%.5f",(float)peak);
301 gtk_label_set_text(rd->peak_labels[c],buf);
302 if (((float)peak) > rd->maxpeak_values[c] )
303 {
304 rd->maxpeak_values[c] = (float)peak;
305 gtk_label_set_text(rd->maxpeak_labels[c],buf);
306 }
307 g_snprintf(buf,sizeof(buf),"%.5f",(float)rms);
308 gtk_label_set_text(rd->rms_labels[c],buf);
309 /* This probably needs some tweaking but has to do for now. */
310 if (clip_amount == 0)
311 gtk_label_set_text(rd->clip_labels[c],_("None"));
312 else if (((float)clip_size)/((float)clip_amount) < 2.0
313 || (clip_size < rd->analysis_samples / (d * 100)))
314 gtk_label_set_text(rd->clip_labels[c],_("Mild"));
315 else
316 gtk_label_set_text(rd->clip_labels[c],_("Heavy"));
317 }
318 if (rd->tf) {
319 get_time(rd->current_format->samplerate,
320 rd->written_bytes/rd->current_format->samplebytes,
321 0,buf,default_time_mode);
322 gtk_label_set_text(rd->time_label,buf);
323 g_snprintf(buf,200,"%" OFF_T_FORMAT "",
324 (OFF_T_FTYPE)rd->written_bytes);
325 gtk_label_set_text(rd->bytes_label,buf);
326 get_time_s(rd->current_format->samplerate,
327 rd->written_bytes/rd->current_format->samplebytes,
328 0, buf);
329 if ( strcmp(buf, GTK_WINDOW(rd)->title) ) {
330 gtk_window_set_title(GTK_WINDOW(rd),buf);
331 /* Also update the remaining time here */
332 set_limit_label(rd);
333 }
334 i = input_overrun_count();
335 if (i>=0) {
336 i -= rd->overruns_before_start;
337 while (i > rd->overruns) {
338 if (rd->overruns < 10)
339 rd->overrun_locs[rd->overruns] = rd->written_bytes;
340 rd->overruns++;
341 }
342 g_snprintf(buf,sizeof(buf),"%d",i);
343 gtk_label_set_text(rd->overruns_label,buf);
344 }
345
346 }
347 return TRUE;
348 }
349
input_ready_func(void)350 void input_ready_func(void)
351 {
352 process_input(current_dialog);
353 }
354
record_dialog_format_changed(Combo * combo,gpointer user_data)355 static void record_dialog_format_changed(Combo *combo, gpointer user_data)
356 {
357 RECORD_DIALOG(user_data)->format_changed = TRUE;
358 }
359
other_dialog_get_name(void)360 static gchar *other_dialog_get_name(void)
361 {
362 gchar *c,*d;
363 c = (gchar *)gtk_entry_get_text(other_dialog.name_entry);
364 while (*c == ' ') c++; /* Skip beginning spaces */
365 if (*c == 0) return NULL;
366 c = g_strdup(c);
367 /* Trim ending spaces */
368 d = strchr(c,0);
369 d--;
370 while (*d == ' ') { *d = 0; d--; }
371 return c;
372 }
373
other_dialog_ok(GtkButton * button,gpointer user_data)374 static void other_dialog_ok(GtkButton *button, gpointer user_data)
375 {
376 RecordDialog *rd = RECORD_DIALOG(user_data);
377 Dataformat df;
378 gchar *c;
379
380 if (format_selector_check(other_dialog.fs)) return;
381
382 format_selector_get(other_dialog.fs,&df);
383
384 c = other_dialog_get_name();
385 if (c != NULL && compare_preset(c,&df))
386 record_format_combo_set_named_preset(rd->format_combo,c);
387 else
388 record_format_combo_set_format(rd->format_combo,&df);
389 g_free(c);
390 }
391
other_dialog_addpreset(GtkButton * button,gpointer user_data)392 static void other_dialog_addpreset(GtkButton *button, gpointer user_data)
393 {
394 gchar *name;
395 Dataformat df;
396
397 if (format_selector_check(other_dialog.fs)) return;
398 format_selector_get(other_dialog.fs,&df);
399
400 name = other_dialog_get_name();
401 g_assert(name != NULL);
402
403 set_preset(name,&df);
404 /* b = record_format_combo_set_named_preset(rd->format_combo,name);
405 g_assert(b); */
406 g_free(name);
407 }
408
other_dialog_delete(GtkWidget * widget,GdkEvent * event,gpointer user_data)409 static gboolean other_dialog_delete(GtkWidget *widget, GdkEvent *event,
410 gpointer user_data)
411 {
412 return FALSE;
413 }
414
other_dialog_name_changed(GtkEditable * editable,gpointer user_data)415 static void other_dialog_name_changed(GtkEditable *editable,
416 gpointer user_data)
417 {
418 gchar *c;
419 GList *l;
420 RecordFormat *rf;
421
422 c = other_dialog_get_name();
423 for (l=preset_list->list; l!=NULL; l=l->next) {
424 rf = (RecordFormat *)(l->data);
425 if (rf->name != NULL && c != NULL && !strcmp(rf->name,c))
426 break;
427 }
428 if (l != NULL) {
429 gtk_label_set_text(other_dialog.set_button_label,
430 _("Update preset"));
431 gtk_widget_set_sensitive(GTK_WIDGET(other_dialog.set_button_button),
432 TRUE);
433 } else if (c != NULL) {
434 gtk_label_set_text(other_dialog.set_button_label,
435 _("Add preset"));
436 gtk_widget_set_sensitive(GTK_WIDGET(other_dialog.set_button_button),
437 TRUE);
438 } else {
439 gtk_widget_set_sensitive(GTK_WIDGET(other_dialog.set_button_button),
440 FALSE);
441 }
442 g_free(c);
443 }
444
other_dialog_build_preset_list(RecordDialog * rd)445 static GtkWidget *other_dialog_build_preset_list(RecordDialog *rd)
446 {
447 GtkWidget *a,*x=NULL;
448 GList *l;
449 RecordFormat *rf;
450 gchar *n;
451 n = record_format_combo_get_preset_name(rd->format_combo);
452 for (l=preset_list->list; l!=NULL; l=l->next) {
453 rf = (RecordFormat *)(l->data);
454 if (rf->name == NULL) continue;
455 a = gtk_list_item_new_with_label(rf->name);
456 gtk_container_add(GTK_CONTAINER(other_dialog.preset_list),a);
457 gtk_object_set_data(GTK_OBJECT(a),"fmt",rf);
458 if (n != NULL && rf->name != NULL && !strcmp(n,rf->name))
459 x = a;
460 }
461 return x;
462 }
463
other_dialog_select_child(GtkList * list,GtkWidget * widget,gpointer user_data)464 static void other_dialog_select_child(GtkList *list, GtkWidget *widget,
465 gpointer user_data)
466 {
467 RecordFormat *rf;
468 rf = gtk_object_get_data(GTK_OBJECT(widget),"fmt");
469 format_selector_set(other_dialog.fs, &(rf->fmt));
470 gtk_entry_set_text(other_dialog.name_entry, rf->name);
471 }
472
other_dialog_preset_item_added(ListObject * lo,RecordFormat * rf,gpointer user_data)473 static void other_dialog_preset_item_added(ListObject *lo, RecordFormat *rf,
474 gpointer user_data)
475 {
476 GtkWidget *a;
477 a = gtk_list_item_new_with_label(rf->name);
478 gtk_container_add(GTK_CONTAINER(other_dialog.preset_list),a);
479 gtk_object_set_data(GTK_OBJECT(a),"fmt",rf);
480 gtk_widget_show(a);
481 }
482
other_dialog_preset_item_removed(ListObject * lo,gpointer item,gpointer user_data)483 static void other_dialog_preset_item_removed(ListObject *lo, gpointer item,
484 gpointer user_data)
485 {
486 GList *l;
487 gpointer p;
488 l = gtk_container_get_children(GTK_CONTAINER(other_dialog.preset_list));
489 for (; l!=NULL; l=l->next) {
490 p = gtk_object_get_data(GTK_OBJECT(l->data),"fmt");
491 if (p == item) {
492 gtk_container_remove(GTK_CONTAINER(other_dialog.preset_list),
493 GTK_WIDGET(l->data));
494 break;
495 }
496 }
497 g_list_free(l);
498
499 }
500
other_format_dialog(RecordFormatCombo * rfc,RecordDialog * rd)501 static void other_format_dialog(RecordFormatCombo *rfc, RecordDialog *rd)
502 {
503 GtkWidget *a,*b,*c,*d,*e,*f,*item;
504 GtkAccelGroup* ag;
505 GtkRequisition req;
506 #if GTK_MAJOR_VERSION > 1
507 static GtkWindowGroup *wg = NULL;
508 #endif
509
510
511 ag = gtk_accel_group_new();
512
513 other_dialog.wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_DIALOG));
514 gtk_window_set_title(other_dialog.wnd,_("Custom format"));
515 #if GTK_MAJOR_VERSION < 2
516 gtk_window_set_modal(other_dialog.wnd,TRUE);
517 #else
518 if (wg == NULL) wg = gtk_window_group_new();
519 gtk_window_group_add_window(wg,other_dialog.wnd);
520 #endif
521 gtk_window_set_transient_for(other_dialog.wnd,GTK_WINDOW(rd));
522
523 other_dialog.fs = FORMAT_SELECTOR(format_selector_new(TRUE));
524
525 other_dialog.name_entry = GTK_ENTRY(gtk_entry_new());
526
527 other_dialog.preset_list = GTK_LIST(gtk_list_new());
528 item = other_dialog_build_preset_list(rd);
529 gtk_signal_connect_while_alive
530 (GTK_OBJECT(preset_list),"item_added",
531 GTK_SIGNAL_FUNC(other_dialog_preset_item_added),other_dialog.wnd,
532 GTK_OBJECT(other_dialog.wnd));
533 gtk_signal_connect_while_alive
534 (GTK_OBJECT(preset_list),"item_removed",
535 GTK_SIGNAL_FUNC(other_dialog_preset_item_removed),other_dialog.wnd,
536 GTK_OBJECT(other_dialog.wnd));
537
538 a = GTK_WIDGET(other_dialog.wnd);
539 gtk_container_set_border_width(GTK_CONTAINER(a),10);
540 gtk_signal_connect(GTK_OBJECT(a),"delete_event",
541 GTK_SIGNAL_FUNC(other_dialog_delete),NULL);
542 b = gtk_vbox_new(FALSE,6);
543 gtk_container_add(GTK_CONTAINER(a),b);
544 c = gtk_hbox_new(FALSE,6);
545 gtk_container_add(GTK_CONTAINER(b),c);
546 d = gtk_vbox_new(FALSE,6);
547 gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,0);
548 e = GTK_WIDGET(other_dialog.fs);
549 gtk_box_pack_start(GTK_BOX(d),e,FALSE,FALSE,0);
550 e = gtk_hseparator_new();
551 gtk_box_pack_start(GTK_BOX(d),e,FALSE,FALSE,0);
552 e = gtk_label_new(_("The sign and endian-ness can usually be left at their "
553 "defaults, but should be changed if you're unable to "
554 "record or get bad sound."));
555 gtk_label_set_line_wrap(GTK_LABEL(e),TRUE);
556 gtk_box_pack_start(GTK_BOX(d),e,FALSE,FALSE,0);
557
558 /*
559 c = gtk_label_new(_("To add this format to the presets, enter a name "
560 "below. Otherwise, leave it blank."));
561 gtk_label_set_line_wrap(GTK_LABEL(c),TRUE);
562 gtk_container_add(GTK_CONTAINER(b),c);
563 */
564 e = gtk_hbox_new(FALSE,4);
565 gtk_box_pack_end(GTK_BOX(d),e,FALSE,FALSE,0);
566 f = gtk_label_new(_("Name :"));
567 gtk_container_add(GTK_CONTAINER(e),f);
568 f = GTK_WIDGET(other_dialog.name_entry);
569 gtk_container_add(GTK_CONTAINER(e),f);
570 e = gtk_hseparator_new();
571 gtk_box_pack_end(GTK_BOX(d),e,FALSE,FALSE,0);
572 d = gtk_vseparator_new();
573 gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,0);
574
575 d = gtk_vbox_new(FALSE,6);
576 gtk_container_add(GTK_CONTAINER(c),d);
577
578 e = gtk_label_new(_("Presets:"));
579 gtk_box_pack_start(GTK_BOX(d),e,FALSE,FALSE,0);
580
581 e = gtk_scrolled_window_new(NULL,NULL);
582 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(e),GTK_POLICY_NEVER,
583 GTK_POLICY_AUTOMATIC);
584 gtk_container_add(GTK_CONTAINER(d),e);
585
586 f = GTK_WIDGET(other_dialog.preset_list);
587 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(e),f);
588
589 c = gtk_hseparator_new();
590 gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
591 c = gtk_hbutton_box_new();
592 gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
593 d = gtk_button_new_with_label(_("Set format"));
594 gtk_widget_add_accelerator (d, "clicked", ag, GDK_KP_Enter, 0,
595 (GtkAccelFlags) 0);
596 gtk_widget_add_accelerator (d, "clicked", ag, GDK_Return, 0,
597 (GtkAccelFlags) 0);
598 gtk_container_add(GTK_CONTAINER(c),d);
599 gtk_signal_connect(GTK_OBJECT(d),"clicked",
600 GTK_SIGNAL_FUNC(other_dialog_ok),rd);
601 d = gtk_button_new_with_label(_("Add/Update preset"));
602 gtk_widget_set_sensitive(d,FALSE);
603 gtk_widget_size_request(d,&req);
604 gtk_widget_set_size_request(d,req.width,req.height);
605 gtk_container_add(GTK_CONTAINER(c),d);
606 gtk_signal_connect(GTK_OBJECT(d),"clicked",
607 GTK_SIGNAL_FUNC(other_dialog_addpreset),rd);
608 other_dialog.set_button_button = GTK_BUTTON(d);
609 other_dialog.set_button_label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(d)));
610 d = gtk_button_new_with_label(_("Close"));
611 gtk_widget_add_accelerator (d, "clicked", ag, GDK_Escape, 0,
612 (GtkAccelFlags) 0);
613 gtk_container_add(GTK_CONTAINER(c),d );
614 gtk_signal_connect_object(GTK_OBJECT(d),"clicked",
615 GTK_SIGNAL_FUNC(gtk_widget_destroy),
616 GTK_OBJECT(a));
617 gtk_widget_show_all(a);
618 gtk_window_add_accel_group(GTK_WINDOW (a), ag);
619
620 gtk_signal_connect(GTK_OBJECT(other_dialog.wnd),"delete_event",
621 GTK_SIGNAL_FUNC(other_dialog_delete),NULL);
622 gtk_signal_connect(GTK_OBJECT(other_dialog.preset_list),"select_child",
623 GTK_SIGNAL_FUNC(other_dialog_select_child),rd);
624 gtk_signal_connect(GTK_OBJECT(other_dialog.name_entry),"changed",
625 GTK_SIGNAL_FUNC(other_dialog_name_changed),rd);
626
627 if (item != NULL) gtk_list_select_child(other_dialog.preset_list,item);
628
629 gtk_signal_connect_object_while_alive(GTK_OBJECT(rd),"destroy",
630 GTK_SIGNAL_FUNC(gtk_widget_destroy),
631 GTK_OBJECT(other_dialog.wnd));
632 }
633
update_limit(RecordDialog * rd)634 static void update_limit(RecordDialog *rd)
635 {
636 gdouble d;
637 if (rd->tf == NULL) return;
638 d = (gdouble)rd->limit_seconds;
639 d *= (gdouble)(rd->current_format->samplerate);
640 d *= (gdouble)(rd->current_format->samplebytes);
641 rd->limit_bytes = (off_t)d;
642 set_limit_label(rd);
643 }
644
set_time_limit(GtkButton * button,gpointer user_data)645 static void set_time_limit(GtkButton *button, gpointer user_data)
646 {
647 gfloat f;
648 gchar buf[64];
649 RecordDialog *rd = RECORD_DIALOG(user_data);
650
651 f = parse_time((gchar *)gtk_entry_get_text(rd->limit_entry));
652
653 if (f < 0.0) {
654 popup_error(_("Invalid time value. Time must be specified in the form"
655 " (HH')MM:SS(.mmmm)"));
656 return;
657 }
658
659 rd->limit_record = TRUE;
660 rd->limit_seconds = f;
661 get_time_l(1000,(off_t)(f*1000.0),(off_t)(f*1000.0),buf);
662 gtk_label_set_text(rd->limit_set_label, buf);
663 gtk_widget_set_sensitive(GTK_WIDGET(rd->disable_limit_button),TRUE);
664 update_limit(rd);
665 }
666
disable_time_limit(GtkButton * button,gpointer user_data)667 static void disable_time_limit(GtkButton *button, gpointer user_data)
668 {
669 RecordDialog *rd = RECORD_DIALOG(user_data);
670 rd->limit_record = FALSE;
671 gtk_label_set_text(rd->limit_set_label,_("(no limit)"));
672 gtk_widget_set_sensitive(GTK_WIDGET(rd->disable_limit_button),FALSE);
673 }
674
record_dialog_set_format(RecordDialog * rd)675 static gboolean record_dialog_set_format(RecordDialog *rd)
676 {
677 Dataformat *df;
678 GtkWidget *a,*b;
679 gint i;
680
681 /* printf("record_dialog_set_format: %s fresh=%d\n",rf->name,
682 rd->format_combo_fresh); */
683 input_stop();
684 rd->current_format = NULL;
685 if (rd->vu_frame->child != NULL)
686 gtk_container_remove(GTK_CONTAINER(rd->vu_frame),
687 rd->vu_frame->child);
688 g_free(rd->peak_labels);
689 g_free(rd->maxpeak_labels);
690 g_free(rd->rms_labels);
691 g_free(rd->clip_labels);
692 g_free(rd->meters);
693 rd->peak_labels = NULL;
694 rd->maxpeak_labels = NULL;
695 rd->rms_labels = NULL;
696 rd->clip_labels = NULL;
697 rd->meters = NULL;
698 gtk_label_set_text(rd->status_label,_("Format not selected"));
699 gtk_widget_set_sensitive(rd->record_button,FALSE);
700 gtk_widget_set_sensitive(rd->reset_button,FALSE);
701
702 df = record_format_combo_get_format(rd->format_combo);
703 i = input_select_format(df,FALSE,input_ready_func);
704 if (i < 0) {
705 user_error(_("This format is not supported by the input driver!"));
706 return TRUE;
707 } else if (i > 0)
708 return TRUE;
709
710 gtk_label_set_text(rd->status_label,_("Ready for recording"));
711 record_format_combo_store(rd->format_combo);
712 rd->current_format = &(rd->format_combo->stored_selection_format);
713 rd->peak_labels = g_malloc(df->channels*sizeof(GtkLabel *));
714 rd->maxpeak_labels = g_malloc(df->channels*sizeof(GtkLabel *));
715 rd->clip_labels = g_malloc(df->channels*sizeof(GtkLabel *));
716 rd->rms_labels = g_malloc(df->channels*sizeof(GtkLabel *));
717 rd->meters = g_malloc(df->channels*sizeof(VuMeter *));
718 memset(rd->maxpeak_values,0,sizeof(rd->maxpeak_values));
719 a = gtk_table_new(6*((df->channels+1)/2),4,FALSE);
720 gtk_container_set_border_width(GTK_CONTAINER(a),4);
721
722 for (i=0; i*2<df->channels; i++) {
723 attach_label(_("Peak: "),a,i*6+2,0);
724 attach_label(_("Peak max: "),a,i*6+3,0);
725 attach_label(_("RMS: "),a,i*6+4,0);
726 attach_label(_("Clipping: "),a,i*6+5,0);
727 }
728 for (i=0; i<df->channels; i++) {
729 b = gtk_label_new(channel_name(i,df->channels));
730 gtk_table_attach(GTK_TABLE(a),b,(i&1)+1,(i&1)+2,(i/2)*6+0,(i/2)*6+1,
731 0,0,0,0);
732 b = vu_meter_new(0.0);
733 rd->meters[i] = VU_METER(b);
734 gtk_table_attach(GTK_TABLE(a),b,(i&1)+1,(i&1)+2,(i/2)*6+1,(i/2)*6+2,
735 0,0,0,0);
736 rd->peak_labels[i] = attach_label("",a,(i/2)*6+2,(i&1)+1);
737 rd->maxpeak_labels[i] = attach_label("",a,(i/2)*6+3,(i&1)+1);
738 rd->rms_labels[i] = attach_label("",a,(i/2)*6+4,(i&1)+1);
739 rd->clip_labels[i] = attach_label(_("None"),a,(i/2)*6+5,(i&1)+1);
740 }
741
742 gtk_table_set_col_spacings(GTK_TABLE(a),5);
743 gtk_table_set_row_spacings(GTK_TABLE(a),3);
744 gtk_container_add(GTK_CONTAINER(rd->vu_frame),a);
745 gtk_widget_show_all(a);
746
747 gtk_widget_set_sensitive(rd->record_button,TRUE);
748 gtk_widget_set_sensitive(rd->reset_button,TRUE);
749
750 /* Create a 2 second ring buffer */
751 if (rd->databuf != NULL) {
752 ringbuf_free(rd->databuf);
753 g_free(rd->analysis_buf);
754 g_free(rd->analysis_sbuf);
755 }
756 rd->databuf = ringbuf_new(2*df->samplebytes*df->samplerate);
757 /* Do analysis on 0.1 s parts. */
758 rd->analysis_bytes = df->samplebytes * df->samplerate / 10;
759 rd->analysis_samples = df->channels * df->samplerate / 10;
760 rd->analysis_buf = g_malloc(rd->analysis_bytes);
761 rd->analysis_sbuf = g_malloc(sizeof(sample_t) * rd->analysis_samples);
762
763 /* Call process_input manually one time here
764 * (required for some sound drivers to start recording) */
765 process_input(rd);
766
767 return FALSE;
768 }
769
check_format_change(RecordDialog * rd)770 static void check_format_change(RecordDialog *rd)
771 {
772 if (!rd->format_changed)
773 return;
774
775 rd->format_changed = FALSE;
776 record_dialog_set_format(rd);
777 }
778
record_dialog_start(GtkButton * button,gpointer user_data)779 static void record_dialog_start(GtkButton *button, gpointer user_data)
780 {
781 RecordDialog *rd = RECORD_DIALOG(user_data);
782 GtkRequisition req;
783 int i;
784 gchar *c;
785
786 if (rd->tf != NULL) {
787 /* This is a hack to prevent the window from resizing when we
788 * change the button's caption */
789 gtk_widget_size_request(rd->record_button,&req);
790 gtk_widget_set_usize(rd->record_button,req.width,req.height);
791
792 /* Toggle pause mode */
793 rd->paused = !rd->paused;
794 gtk_label_set_text(GTK_LABEL(GTK_BIN(rd->record_button)->child),
795 rd->paused?_("Resume recording"):_("Pause recording"));
796 gtk_label_set_text(rd->status_label,rd->paused?
797 translate_strip(N_("RecordStatus|Paused")):
798 translate_strip(N_("RecordStatus|Recording")));
799 return;
800 }
801
802 inifile_set_gboolean("limitRecord",rd->limit_record);
803 inifile_set_gfloat( "limitSecs",rd->limit_seconds);
804
805 c = record_format_combo_get_preset_name(rd->format_combo);
806 inifile_set("lastRecordFormat",c);
807 if (c == NULL) {
808 dataformat_save_to_inifile("lastRecordFormat",
809 record_format_combo_get_format
810 (rd->format_combo), TRUE);
811 }
812
813 gtk_widget_set_sensitive(GTK_WIDGET(rd->format_combo),FALSE);
814 gtk_label_set_text(GTK_LABEL(GTK_BIN(rd->record_button)->child),
815 _("Pause recording"));
816 rd->paused = FALSE;
817 gtk_label_set_text(GTK_LABEL(GTK_BIN(rd->close_button)->child),
818 _("Finish"));
819 rd->tf = tempfile_init(rd->current_format,TRUE);
820 rd->written_bytes = 0;
821 i = input_overrun_count();
822 gtk_label_set_text(rd->status_label,
823 translate_strip(N_("RecordStatus|Recording")));
824 if (i>-1) {
825 rd->overruns_before_start = i;
826 gtk_label_set_text(rd->overruns_title,_("Overruns: "));
827 }
828 rd->overruns = 0;
829 gtk_label_set_text(rd->bytes_text_label,_("Bytes written: "));
830 gtk_label_set_text(rd->limit_text_label,_("Auto stop in: "));
831 update_limit(rd);
832 }
833
record_dialog_close(GtkButton * button,gpointer user_data)834 static void record_dialog_close(GtkButton *button, gpointer user_data)
835 {
836 record_dialog_stopflag = TRUE;
837 }
838
record_dialog_delete_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)839 static gboolean record_dialog_delete_event(GtkWidget *widget, GdkEvent *event,
840 gpointer user_data)
841 {
842 record_dialog_stopflag = TRUE;
843 return TRUE;
844 }
845
record_dialog_init(RecordDialog * obj)846 void record_dialog_init(RecordDialog *obj)
847 {
848 GtkWidget *a,*b,*c,*d,*e;
849 GtkAccelGroup* ag;
850 GtkRequisition req;
851 gchar limitbuf[64];
852 gchar *s1;
853 GList *dp;
854 gboolean complete;
855 Dataformat df,*dfp;
856
857 ag = gtk_accel_group_new();
858
859 obj->format_changed = FALSE;
860 obj->current_format = NULL;
861 obj->databuf = NULL;
862 obj->meters = NULL;
863 obj->peak_labels = obj->maxpeak_labels = obj->clip_labels = NULL;
864 obj->rms_labels = NULL;
865 obj->tf = NULL;
866 obj->written_bytes = 0;
867 obj->analysis_buf = NULL;
868 obj->analysis_sbuf = NULL;
869 obj->limit_record = inifile_get_gboolean("limitRecord",FALSE);
870 obj->limit_seconds = inifile_get_gfloat("limitSecs",3600.0);
871 if (obj->limit_seconds < 0.0) obj->limit_seconds = 3600.0;
872 get_time_l(1000,(off_t)(obj->limit_seconds*1000.0),0,limitbuf);
873
874 gtk_window_set_title(GTK_WINDOW(obj),_("Record"));
875 gtk_window_set_modal(GTK_WINDOW(obj),TRUE);
876 gtk_window_set_default_size(GTK_WINDOW(obj),320,400);
877 gtk_window_set_position(GTK_WINDOW(obj),GTK_WIN_POS_CENTER);
878 gtk_container_set_border_width(GTK_CONTAINER(obj),10);
879
880 gtk_signal_connect(GTK_OBJECT(obj),"delete_event",
881 GTK_SIGNAL_FUNC(record_dialog_delete_event),NULL);
882
883
884
885 build_preset_list();
886 dp = input_supported_formats(&complete);
887 obj->driver_presets = list_object_new_from_list(dp,FALSE);
888 gtk_object_ref(GTK_OBJECT(obj->driver_presets));
889 gtk_object_sink(GTK_OBJECT(obj->driver_presets));
890
891 /* Add components */
892 a = gtk_vbox_new(FALSE,10);
893 gtk_container_add(GTK_CONTAINER(obj),a);
894 b = gtk_frame_new(_("Recording settings"));
895 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
896 c = gtk_vbox_new(FALSE,0);
897 gtk_container_set_border_width(GTK_CONTAINER(c),5);
898 gtk_container_add(GTK_CONTAINER(b),c);
899 d = gtk_hbox_new(FALSE,0);
900 gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,0);
901 e = gtk_label_new(_("Sample format: "));
902 gtk_box_pack_start(GTK_BOX(d),e,FALSE,FALSE,0);
903
904 e = record_format_combo_new(complete ? list_object_new(FALSE):preset_list,
905 obj->driver_presets, !complete);
906 gtk_box_pack_start(GTK_BOX(d),e,TRUE,TRUE,0);
907 obj->format_combo = RECORD_FORMAT_COMBO(e);
908 gtk_signal_connect(GTK_OBJECT(e),"format_changed",
909 GTK_SIGNAL_FUNC(record_dialog_format_changed),obj);
910 gtk_signal_connect(GTK_OBJECT(e),"format_dialog_request",
911 GTK_SIGNAL_FUNC(other_format_dialog),obj);
912
913 d = gtk_hbox_new(FALSE,3);
914 gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,6);
915
916 e = gtk_label_new(_("Time limit: "));
917 gtk_box_pack_start(GTK_BOX(d),e,FALSE,FALSE,0);
918 e = gtk_label_new(_("(no limit)"));
919 obj->limit_set_label = GTK_LABEL(e);
920 if (obj->limit_record)
921 gtk_label_set_text(obj->limit_set_label, limitbuf);
922 gtk_box_pack_start(GTK_BOX(d),e,FALSE,FALSE,0);
923
924 e = gtk_button_new_with_label(_("Disable"));
925 obj->disable_limit_button = GTK_BUTTON(e);
926 gtk_widget_set_sensitive(GTK_WIDGET(e),obj->limit_record);
927 gtk_signal_connect(GTK_OBJECT(e),"clicked",
928 GTK_SIGNAL_FUNC(disable_time_limit),obj);
929 gtk_box_pack_end(GTK_BOX(d),e,FALSE,FALSE,0);
930 e = gtk_button_new_with_label(_("Set"));
931 gtk_signal_connect(GTK_OBJECT(e),"clicked",
932 GTK_SIGNAL_FUNC(set_time_limit),obj);
933 gtk_box_pack_end(GTK_BOX(d),e,FALSE,FALSE,0);
934 e = gtk_entry_new();
935 obj->limit_entry = GTK_ENTRY(e);
936 gtk_entry_set_text(obj->limit_entry, limitbuf );
937 /* Max length = "hhh'mm:ss.mmm" */
938 gtk_entry_set_max_length( obj->limit_entry, 14);
939 gtk_box_pack_end(GTK_BOX(d),e,FALSE,FALSE,0);
940
941
942 b = gtk_frame_new(_("Input levels"));
943 obj->vu_frame = GTK_BIN(b);
944 gtk_box_pack_start(GTK_BOX(a),b,TRUE,TRUE,0);
945 b = gtk_table_new(3,4,FALSE);
946 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
947 attach_label(_("Recording status: "),b,0,0);
948 obj->status_label = GTK_LABEL(gtk_label_new(_("Format not selected")));
949 gtk_table_attach(GTK_TABLE(b),GTK_WIDGET(obj->status_label),1,4,0,1,
950 GTK_FILL,0,0,0);
951 gtk_misc_set_alignment(GTK_MISC(obj->status_label),0.0,0.5);
952 attach_label(_("Time recorded: "),b,1,0);
953 obj->time_label = attach_label(_("N/A"),b,1,1);
954 gtk_widget_size_request(GTK_WIDGET(obj->time_label),&req);
955 /* Stops wobble during recording */
956 gtk_widget_set_usize(GTK_WIDGET(obj->time_label),150,req.height);
957
958 obj->limit_text_label = attach_label("",b,2,0);
959 obj->limit_label = attach_label("",b,2,1);
960 obj->bytes_text_label = attach_label("",b,1,2);
961 obj->bytes_label = attach_label("",b,1,3);
962 obj->overruns_title = attach_label("",b,2,2);
963 obj->overruns_label = attach_label("",b,2,3);
964 b = gtk_hbutton_box_new();
965 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
966 c = gtk_button_new_with_label(_("Start recording"));
967 gtk_widget_add_accelerator (c, "clicked", ag, GDK_KP_Enter, 0,
968 (GtkAccelFlags) 0);
969 gtk_widget_add_accelerator (c, "clicked", ag, GDK_Return, 0,
970 (GtkAccelFlags) 0);
971 gtk_widget_set_sensitive(c,FALSE);
972 gtk_signal_connect(GTK_OBJECT(c),"clicked",
973 GTK_SIGNAL_FUNC(record_dialog_start),obj);
974 obj->record_button = GTK_WIDGET(c);
975 gtk_container_add(GTK_CONTAINER(b),c);
976
977 c = gtk_button_new_with_label(_("Reset max peaks"));
978 gtk_widget_set_sensitive(c,FALSE);
979 gtk_signal_connect(GTK_OBJECT(c),"clicked", GTK_SIGNAL_FUNC(reset_peaks),obj);
980 obj->reset_button = GTK_WIDGET(c);
981 gtk_container_add(GTK_CONTAINER(b),c);
982
983 c = gtk_button_new_with_label(_("Launch mixer"));
984 gtk_signal_connect(GTK_OBJECT(c),"clicked",GTK_SIGNAL_FUNC(launch_mixer),
985 NULL);
986 gtk_container_add(GTK_CONTAINER(b),c);
987 c = gtk_button_new_with_label(_("Close"));
988 gtk_widget_add_accelerator (c, "clicked", ag, GDK_Escape, 0,
989 (GtkAccelFlags) 0);
990 gtk_signal_connect(GTK_OBJECT(c),"clicked",
991 GTK_SIGNAL_FUNC(record_dialog_close),obj);
992 gtk_container_add(GTK_CONTAINER(b),c);
993 obj->close_button = c;
994
995 gtk_widget_show_all(a);
996 gtk_window_add_accel_group(GTK_WINDOW (obj), ag);
997
998 /* Special case: Only one format supported. Choose that format and
999 * make the format combo insensitive */
1000 if (complete && list_object_get_size(obj->driver_presets)==1) {
1001 dfp = (Dataformat *)list_object_get(obj->driver_presets,0);
1002 record_format_combo_set_format(obj->format_combo,dfp);
1003 gtk_widget_set_sensitive(GTK_WIDGET(obj->format_combo),FALSE);
1004 return;
1005 }
1006
1007 /* Set the last used format */
1008 s1 = inifile_get("lastRecordFormat",NULL);
1009 if (s1 != NULL) {
1010 record_format_combo_set_named_preset(obj->format_combo,s1);
1011 } else {
1012 if (dataformat_get_from_inifile("lastRecordFormat",TRUE,&df))
1013 record_format_combo_set_format(obj->format_combo,&df);
1014 }
1015 }
1016
record_dialog_destroy(GtkObject * obj)1017 static void record_dialog_destroy(GtkObject *obj)
1018 {
1019 RecordDialog *rd = RECORD_DIALOG(obj);
1020 if (rd->databuf) ringbuf_free(rd->databuf);
1021 rd->databuf = NULL;
1022 g_free(rd->analysis_buf);
1023 rd->analysis_buf = NULL;
1024 g_free(rd->analysis_sbuf);
1025 rd->analysis_sbuf = NULL;
1026 parent_class->destroy(obj);
1027 if (rd->driver_presets != NULL) {
1028 list_object_foreach(rd->driver_presets, (GFunc)g_free, NULL);
1029 list_object_clear(rd->driver_presets, FALSE);
1030 gtk_object_unref(GTK_OBJECT(rd->driver_presets));
1031 rd->driver_presets = NULL;
1032 }
1033 }
1034
record_dialog_class_init(GtkObjectClass * klass)1035 static void record_dialog_class_init(GtkObjectClass *klass)
1036 {
1037 parent_class = gtk_type_class(gtk_window_get_type());
1038 klass->destroy = record_dialog_destroy;
1039 }
1040
record_dialog_get_type(void)1041 GtkType record_dialog_get_type(void)
1042 {
1043 static GtkType id = 0;
1044 if (!id) {
1045 GtkTypeInfo info = {
1046 "RecordDialog",
1047 sizeof(RecordDialog),
1048 sizeof(RecordDialogClass),
1049 (GtkClassInitFunc) record_dialog_class_init,
1050 (GtkObjectInitFunc) record_dialog_init
1051 };
1052 id = gtk_type_unique(gtk_window_get_type(),&info);
1053 }
1054 return id;
1055 }
1056
record_dialog_execute(int * noverruns,off_t overrun_locs[10])1057 Chunk *record_dialog_execute(int *noverruns, off_t overrun_locs[10])
1058 {
1059 RecordDialog *rd;
1060 Chunk *ds;
1061 int i;
1062
1063 rd = RECORD_DIALOG(gtk_type_new(record_dialog_get_type()));
1064 record_dialog_stopflag = FALSE;
1065 current_dialog = rd;
1066 gtk_widget_show(GTK_WIDGET(rd));
1067 while (!record_dialog_stopflag) {
1068 mainloop();
1069 check_format_change(rd);
1070 }
1071 if (rd->tf != NULL) {
1072 input_stop_hint();
1073 i = 0; /* Just to avoid infinite loops */
1074 while (process_input(rd) && i<128) { i++; }
1075 input_stop();
1076 ds = tempfile_finished(rd->tf);
1077 *noverruns = rd->overruns;
1078 memcpy(overrun_locs,rd->overrun_locs,10*sizeof(off_t));
1079 gtk_widget_destroy(GTK_WIDGET(rd));
1080 return ds;
1081 } else {
1082 input_stop();
1083 gtk_widget_destroy(GTK_WIDGET(rd));
1084 return NULL;
1085 }
1086 }
1087