changeset 31874:f4a1df87ce91

This patch adds to Pidgin's vvconfig plugin a simple dialog, where user can test if the audio input and output that he selected are actually working. Two parameters can be also adjusted here: * Output volume (also changeable during call) * Threshold of silence for silence suppression (see ticket #13180) I developed this feature in conjunction with #13180, but the dialog can also be useful on its own, as there is now no way to try the voice configuration prior the call. If requested, I can split the path. Fixes #13182. committer: John Bailey <rekkanoryo@rekkanoryo.org>
author jakub.adam@ktknet.cz
date Fri, 25 Mar 2011 00:01:43 +0000
parents 585afe54508c
children a056c1eaa1f0
files pidgin/plugins/vvconfig.c
diffstat 1 files changed, 187 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/pidgin/plugins/vvconfig.c	Thu Mar 24 23:46:21 2011 +0000
+++ b/pidgin/plugins/vvconfig.c	Fri Mar 25 00:01:43 2011 +0000
@@ -505,19 +505,22 @@
 	gtk_widget_destroy(GTK_WIDGET(window));
 }
 
+typedef GtkWidget *(*FrameCreateCb)(PurplePlugin *plugin);
+
 static void
 show_config(PurplePluginAction *action)
 {
 	if (!window) {
+		FrameCreateCb create_frame = action->user_data;
 		GtkWidget *vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
 		GtkWidget *hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-		GtkWidget *config_frame = get_plugin_config_frame(NULL);
+		GtkWidget *config_frame = create_frame(NULL);
 		GtkWidget *close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
 
 		gtk_container_add(GTK_CONTAINER(vbox), config_frame);
 		gtk_container_add(GTK_CONTAINER(vbox), hbox);
-		window = pidgin_create_window(_("Voice/Video Settings"),
-			PIDGIN_HIG_BORDER, NULL, TRUE);
+		window = pidgin_create_window(action->label,
+			PIDGIN_HIG_BORDER, NULL, FALSE);
 		g_signal_connect(G_OBJECT(window), "destroy",
 			G_CALLBACK(config_destroy), NULL);
 		g_signal_connect(G_OBJECT(close), "clicked",
@@ -531,6 +534,180 @@
 	gtk_window_present(GTK_WINDOW(window));
 }
 
+static GstElement *
+create_pipeline()
+{
+	GstElement *pipeline = gst_pipeline_new("voicetest");
+	GstElement *src = create_audio_src(NULL, NULL, NULL);
+	GstElement *sink = create_audio_sink(NULL, NULL, NULL);
+	GstElement *volume = gst_element_factory_make("volume", "volume");
+	GstElement *level = gst_element_factory_make("level", "level");
+	GstElement *valve = gst_element_factory_make("valve", "valve");
+
+	gst_bin_add_many(GST_BIN(pipeline), src, volume, level, valve, sink, NULL);
+	gst_element_link_many(src, volume, level, valve, sink, NULL);
+
+	gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
+
+	return pipeline;
+}
+
+static void
+on_volume_change_cb(GtkRange *range, GstBin *pipeline)
+{
+	GstElement *volume;
+
+	g_return_if_fail(pipeline != NULL);
+
+	volume = gst_bin_get_by_name(pipeline, "volume");
+	g_object_set(volume, "volume", gtk_range_get_value(range) / 10.0, NULL);
+}
+
+static gdouble
+gst_msg_db_to_percent(GstMessage *msg, gchar *value_name)
+{
+	const GValue *list;
+	const GValue *value;
+	gdouble value_db;
+	gdouble percent;
+
+	list = gst_structure_get_value(
+				gst_message_get_structure(msg), value_name);
+	value = gst_value_list_get_value(list, 0);
+	value_db = g_value_get_double(value);
+	percent = pow(10, value_db / 20);
+	return (percent > 1.0) ? 1.0 : percent;
+}
+
+typedef struct
+{
+	GtkProgressBar *level;
+	GtkRange *threshold;
+} BusCbCtx;
+
+static gboolean
+gst_bus_cb(GstBus *bus, GstMessage *msg, BusCbCtx *ctx)
+{
+	if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
+		gst_structure_has_name(msg->structure, "level")) {
+
+		GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
+		gchar *name = gst_element_get_name(src);
+
+		if (!strcmp(name, "level")) {
+			gdouble percent;
+			gdouble threshold;
+			GstElement *valve;
+
+			percent = gst_msg_db_to_percent(msg, "rms");
+			gtk_progress_bar_set_fraction(ctx->level, percent * 5);
+
+			percent = gst_msg_db_to_percent(msg, "decay");
+			threshold = gtk_range_get_value(ctx->threshold) / 100.0;
+			valve = gst_bin_get_by_name(GST_BIN(GST_ELEMENT_PARENT(src)), "valve");
+			g_object_set(valve, "drop", (percent < threshold), NULL);
+			g_object_set(ctx->level,
+					"text", (percent < threshold) ? _("DROP") : " ", NULL);
+		}
+
+		g_free(name);
+	}
+
+	return TRUE;
+}
+
+static void
+voice_test_frame_destroy_cb(GtkObject *w, GstElement *pipeline)
+{
+	g_return_if_fail(GST_IS_ELEMENT(pipeline));
+
+	gst_element_set_state(pipeline, GST_STATE_NULL);
+	gst_object_unref(pipeline);
+}
+
+static void
+volume_scale_destroy_cb(GtkRange *volume, gpointer nul)
+{
+	purple_prefs_set_int("/purple/media/audio/volume/input",
+			gtk_range_get_value(volume));
+}
+
+static gchar*
+threshold_value_format_cb(GtkScale *scale, gdouble value)
+{
+	return g_strdup_printf ("%.*f%%", gtk_scale_get_digits(scale), value);
+}
+
+static void
+threshold_scale_destroy_cb(GtkRange *threshold, gpointer nul)
+{
+	purple_prefs_set_int("/purple/media/audio/silence_threshold",
+			gtk_range_get_value(threshold));
+}
+
+static GtkWidget *
+get_voice_test_frame(PurplePlugin *plugin)
+{
+	GtkWidget *vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+	GtkWidget *level = gtk_progress_bar_new();
+	GtkWidget *volume = gtk_hscale_new_with_range(0, 100, 1);
+	GtkWidget *threshold = gtk_hscale_new_with_range(0, 100, 1);
+	GtkWidget *label;
+	GtkTable *table = GTK_TABLE(gtk_table_new(2, 2, FALSE));
+
+	GstElement *pipeline;
+	GstBus *bus;
+	BusCbCtx *ctx;
+
+	g_object_set(vbox, "width-request", 500, NULL);
+
+	gtk_table_set_row_spacings(table, PIDGIN_HIG_BOX_SPACE);
+	gtk_table_set_col_spacings(table, PIDGIN_HIG_BOX_SPACE);
+
+	label = gtk_label_new(_("Volume:"));
+	g_object_set(label, "xalign", 0.0, NULL);
+	gtk_table_attach(table, label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+	gtk_table_attach_defaults(table, volume, 1, 2, 0, 1);
+	label = gtk_label_new(_("Silence threshold:"));
+	g_object_set(label, "xalign", 0.0, "yalign", 1.0, NULL);
+	gtk_table_attach(table, label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+	gtk_table_attach_defaults(table, threshold, 1, 2, 1, 2);
+
+	gtk_container_add(GTK_CONTAINER(vbox), level);
+	gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(table));
+	gtk_widget_show_all(vbox);
+
+	pipeline = create_pipeline();
+	bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
+	gst_bus_add_signal_watch(bus);
+	ctx = g_new(BusCbCtx, 1);
+	ctx->level = GTK_PROGRESS_BAR(level);
+	ctx->threshold = GTK_RANGE(threshold);
+	g_signal_connect_data(bus, "message", G_CALLBACK(gst_bus_cb),
+			ctx, (GClosureNotify)g_free, 0);
+	gst_object_unref(bus);
+
+	g_signal_connect(volume, "value-changed",
+			(GCallback)on_volume_change_cb, pipeline);
+
+	gtk_range_set_value(GTK_RANGE(volume),
+			purple_prefs_get_int("/purple/media/audio/volume/input"));
+	gtk_widget_set(volume, "draw-value", FALSE, NULL);
+
+	gtk_range_set_value(GTK_RANGE(threshold),
+			purple_prefs_get_int("/purple/media/audio/silence_threshold"));
+
+	g_signal_connect(vbox, "destroy",
+			G_CALLBACK(voice_test_frame_destroy_cb), pipeline);
+	g_signal_connect(volume, "destroy",
+			G_CALLBACK(volume_scale_destroy_cb), NULL);
+	g_signal_connect(threshold, "format-value",
+			G_CALLBACK(threshold_value_format_cb), NULL);
+	g_signal_connect(threshold, "destroy",
+			G_CALLBACK(threshold_scale_destroy_cb), NULL);
+
+	return vbox;
+}
 
 static GList *
 actions(PurplePlugin *plugin, gpointer context)
@@ -538,8 +715,14 @@
 	GList *l = NULL;
 	PurplePluginAction *act = NULL;
 
-	act = purple_plugin_action_new(_("Voice and Video Settings"),
+	act = purple_plugin_action_new(_("Input and Output Settings"),
 		show_config);
+	act->user_data = get_plugin_config_frame;
+	l = g_list_append(l, act);
+
+	act = purple_plugin_action_new(_("Microphone Test"),
+		show_config);
+	act->user_data = get_voice_test_frame;
 	l = g_list_append(l, act);
 
 	return l;