/* * line level checker functions * * This file is part of ANT (Ant is Not a Telephone) * * Copyright 2002, 2003 Roland Stigge * * ANT is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ANT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ANT; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "config.h" /* regular GNU system includes */ #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include /* GTK */ #include /* own header files */ #include "globals.h" #include "llcheck.h" #include "session.h" #include "gtk.h" /* constants */ /* number of milliseconds for history */ #define HISTORY_TIME 1500 /* * the chain link type for the maximum history */ struct history_t { double value; struct history_t *next; }; /* * return an empty (all zero) history of fixed size (10) */ struct history_t *history_new(void) { struct history_t *root = NULL; struct history_t *temp; int i; for (i = 0; i < 10; i++) { temp = root; root = (struct history_t *)malloc(sizeof(struct history_t)); root->value = 0.0; root->next = temp; } return root; } /* * free memory allocated for history */ void history_free(struct history_t *history) { while (history != NULL) { struct history_t *next = history->next; free(history); history = next; } } /* * appends x to history (rotating) and returns maximum value in whole history * * NOTE: history is assumed to be non-empty */ double history_append(struct history_t **history, double x) { double result = x; struct history_t **hist = &(*history)->next; /* get new maximum */ while (*hist != NULL) { if ((*hist)->value > result) result = (*hist)->value; hist = &(*hist)->next; } /* append */ *hist = *history; /* hist is ** to last element -> append old first one */ *history = (*history)->next; (*hist)->next = NULL; (*hist)->value = x; return result; } /* * sets history to all zero */ void history_reset(struct history_t *history) { while (history) { history->value = 0.0; history = history->next; } } /* * called when OK button is clicked */ static void llcheck_quit(GtkObject *window) { session_t *session = gtk_object_get_data(window, "session"); GtkWidget *bar = gtk_object_get_data(window, "bar"); /* remove own and restore old handlers */ gtk_input_remove(session->gtk_audio_input_tag); session_io_handlers_start(session); /* stop effect */ session_effect_stop(session); llcheck_bar_deinit(bar); gtk_widget_destroy(GTK_WIDGET(window)); session_set_state(session, STATE_READY); } /* * gdk callback on audio input * * input: widget: the llcheck_bar */ static void llcheck_handle_audio_input(gpointer widget, gint fd _U_, GdkInputCondition condition _U_) { session_t *session = gtk_object_get_data(widget, "session"); int i, got; int max = 0; unsigned char sample; got = read(session->audio_fd_in, session->audio_inbuf, session->fragment_size_in); if (got != -1) { for (i = 0; i < got; i += session->audio_sample_size_in) { if (session->audio_sample_size_in == 1) { sample = session->audio_LUT_analyze[ session->audio_LUT_out[(int)(session->audio_inbuf[i])]]; } else { /* audio_sample_size == 2 */ /* multiple byte samples are used "little endian" in int to look up in LUT (see mediation_makeLUT) */ sample = session->audio_LUT_analyze[ session->audio_LUT_out[(int)(session->audio_inbuf[i]) | (int)(session->audio_inbuf[i+1]) << 8]]; } if (abs((int)sample - 128) > max) max = abs((int)sample - 128); } llcheck_bar_set(widget, (double)max / 128); } else { switch (errno) { case EAGAIN: fprintf(stderr, "llcheck_handle_audio_input: " "EAGAIN - no data immediately available.\n"); break; case EBADF: fprintf(stderr, "llcheck_handle_audio_input: " "EBADF - invalid file descriptor.\n"); break; case EINTR: fprintf(stderr, "llcheck_handle_audio_input: EINTR - interrupted by signal.\n"); break; case EIO: fprintf(stderr, "llcheck_handle_audio_input: EIO - hardware error.\n"); break; } } } /* * called when sound is requested in level check window * * input: widget: (play) button * data: session pointer */ static void llcheck_play(GtkWidget *widget, gpointer data) { session_t *session = (session_t *)data; GtkWidget *bar = gtk_object_get_data(GTK_OBJECT(widget), "bar"); if (session->effect != EFFECT_NONE) { /* already playing -> stop feeding */ gtk_input_remove(session->effect_tag); } /* carefully reset audio */ gtk_input_remove(session->gtk_audio_input_tag); session_reset_audio(session); session->gtk_audio_input_tag = gtk_input_add_full(session->audio_fd_in, GDK_INPUT_READ, llcheck_handle_audio_input, NULL, (gpointer) bar, NULL); /* start recording (again) */ read(session->audio_fd_in, session->audio_inbuf, session->fragment_size_in); /* finally, start playing */ session_effect_start(session, EFFECT_TEST); } /* * Returns a line level check bar * input: width, height: size of the whole widget * r, g, b: color of bright bar, dark bar will be some derivative * of it * */ GtkWidget *llcheck_bar_new(gint width, gint height, unsigned char r, unsigned char g, unsigned char b) { GtkWidget *progress_bar; GtkWidget *bar; GtkRcStyle *style; GdkColor color; /* the result to prepare */ bar = gtk_hbox_new(FALSE, 0); gtk_widget_set_size_request(bar, width, height); gtk_object_set_data(GTK_OBJECT(bar), "width", GINT_TO_POINTER(width)); /* progress bar: fast/slow relation progressbar */ progress_bar = gtk_progress_bar_new(); style = gtk_rc_style_new(); style->color_flags[GTK_STATE_PRELIGHT] = GTK_RC_BG; color.red = r * 257; color.green = g * 257; color.blue = b * 257; gdk_colormap_alloc_color(gtk_widget_get_colormap(bar), &color, TRUE, TRUE); style->bg[GTK_STATE_PRELIGHT] = color; style->color_flags[GTK_STATE_NORMAL] = GTK_RC_BG; color.red = r * 257 * 2 / 3; color.green = g * 257 * 2 / 3; color.blue = b * 257 * 2 / 3; gdk_colormap_alloc_color(gtk_widget_get_colormap(bar), &color, TRUE, TRUE); style->bg[GTK_STATE_NORMAL] = color; gtk_widget_modify_style(progress_bar, style); gtk_rc_style_unref(style); gtk_progress_set_percentage(GTK_PROGRESS(progress_bar), 0.0); /* 0..1 */ gtk_object_set_data(GTK_OBJECT(bar), "pbar1", (gpointer) progress_bar); gtk_widget_set_size_request(progress_bar, 1, -1); /* 1..width-1 */ gtk_box_pack_start(GTK_BOX(bar), progress_bar, TRUE, TRUE, 0); gtk_widget_show(progress_bar); /* progress bar: rest */ progress_bar = gtk_progress_bar_new(); style = gtk_rc_style_new(); style->color_flags[GTK_STATE_NORMAL] = GTK_RC_BG; color.red = r * 257 / 3; color.green = g * 257 / 3; color.blue = b * 257 / 3; gdk_colormap_alloc_color(gtk_widget_get_colormap(bar), &color, TRUE, TRUE); style->bg[GTK_STATE_NORMAL] = color; gtk_widget_modify_style(progress_bar, style); gtk_rc_style_unref(style); gtk_progress_set_percentage(GTK_PROGRESS(progress_bar), 0.0); gtk_widget_set_size_request(progress_bar, width - 1, -1); /* 1..width-1 */ gtk_object_set_data(GTK_OBJECT(bar), "pbar2", (gpointer) progress_bar); gtk_object_set_data(GTK_OBJECT(bar), "history", (gpointer) history_new()); gtk_box_pack_start(GTK_BOX(bar), progress_bar, TRUE, TRUE, 0); gtk_widget_show(progress_bar); return bar; } /* * set state of line level check bar and update maximum history * * input: bar: llcheck_bar * max: the current maximum (0.0 <= max <= 1.0) */ void llcheck_bar_set(GtkWidget *bar, double max) { /* the maximum area */ GtkWidget *pbar1 = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(bar), "pbar1"); /* the dark area */ GtkWidget *pbar2 = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(bar), "pbar2"); struct history_t *history = (struct history_t *)gtk_object_get_data(GTK_OBJECT(bar), "history"); double maxmax = history_append(&history, max); int width = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(bar), "width")); /* saving additional width (bar->allocation.width is valid only with shown widgets) */ int pbar1size; double percentage; gtk_object_set_data(GTK_OBJECT(bar), "history", history); pbar1size = maxmax * (width - 2) + 1; gtk_widget_set_size_request(pbar1, pbar1size, -1); gtk_widget_set_size_request(pbar2, width - pbar1size, -1); percentage = maxmax > 0.0 ? max / maxmax : 0.0; gtk_progress_set_percentage(GTK_PROGRESS(pbar1), percentage); } /* * set line level check bar to all zero (history included); */ void llcheck_bar_reset(GtkWidget *bar) { struct history_t *history = (struct history_t *)gtk_object_get_data(GTK_OBJECT(bar), "history"); history_reset(history); llcheck_bar_set(bar, 0.0); } /* * deinit bar (free history mem ...) */ void llcheck_bar_deinit(GtkWidget *bar) { struct history_t *history = gtk_object_get_data(GTK_OBJECT(bar), "history"); history_free(history); } /* * The Line level check callback * -> displays dialog window with level check bar and play button * -> modal */ void llcheck(GtkWidget *widget _U_, gpointer data, guint action _U_) { session_t *session = (session_t *) data; GtkWidget *window; GtkWidget *vbox; GtkWidget *button_box; GtkWidget *button; GtkWidget *label; GtkWidget *bar; /* set to service state -> open audio device */ if (!session_set_state(session, STATE_SERVICE)) { /* the window itself */ window = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(window), _("Line Level Check")); gtk_object_set(GTK_OBJECT(window), "allow_shrink", FALSE, NULL); gtk_object_set(GTK_OBJECT(window), "allow_grow", FALSE, NULL); /* new big vbox */ vbox = gtk_vbox_new(FALSE, 10); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); gtk_widget_show(vbox); /* message label */ label = gtk_label_new(_("Please check the line input level\n" "and adjust it using your favorite\n" "mixer application.\n" "You can also play a sound\n" "to test the sound output.")); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); gtk_widget_show(label); /* the line level check bar */ bar = llcheck_bar_new(300, 20, 200, 0, 0); gtk_box_pack_start(GTK_BOX(vbox), bar, FALSE, FALSE, 0); gtk_widget_show(bar); /* action area */ button_box = gtk_hbutton_box_new(); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->action_area), button_box); gtk_widget_show(button_box); /* the play button */ button = gtk_button_new_with_label(_("Play sound")); gtk_box_pack_start_defaults(GTK_BOX(button_box), button); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(llcheck_play), (gpointer) session ); gtk_widget_show(button); /* save session in window (quit(): audio handling) */ gtk_object_set_data(GTK_OBJECT(window), "session", (gpointer) session); /* save bar in window (quit(): destroy) */ gtk_object_set_data(GTK_OBJECT(window), "bar", (gpointer) bar); /* save session in bar widget (input handling) */ gtk_object_set_data(GTK_OBJECT(bar), "session", (gpointer) session); /* save bar in button (in case play button is pressed) */ gtk_object_set_data(GTK_OBJECT(button), "bar", (gpointer) bar); /* the OK button */ button = gtk_button_new_with_label(_("OK")); gtk_box_pack_start_defaults(GTK_BOX(button_box), button); gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(llcheck_quit), GTK_OBJECT(window)); /* handler for kill function of window */ gtk_signal_connect_object(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(llcheck_quit), GTK_OBJECT(window)); gtk_widget_show(button); gtk_window_set_modal(GTK_WINDOW(window), TRUE); /* remove old and set up own audio input handler */ session_io_handlers_stop(session); session->gtk_audio_input_tag = gtk_input_add_full(session->audio_fd_in, GDK_INPUT_READ, llcheck_handle_audio_input, NULL, (gpointer) bar, NULL); read(session->audio_fd_in, session->audio_inbuf, /* start recording */ session->fragment_size_in); /* show everything */ gtk_widget_show(window); } else { show_audio_error_dialog(); } } /* called when line levels view check button is toggled */ void llcheck_toggle_cb(GtkWidget *widget _U_, gpointer data, guint action _U_) { session_t *session = (session_t *) data; if (GTK_CHECK_MENU_ITEM (session->llcheck_check_menu_item)->active) { gtk_widget_show(session->llcheck); session->option_show_llcheck = 1; } else { gtk_widget_hide(session->llcheck); session->option_show_llcheck = 0; /* shrink if no growing callerid monitor present */ if (!session->option_show_callerid) gtk_window_resize(GTK_WINDOW(session->main_window), 1, 1); } }