summaryrefslogtreecommitdiffstats
path: root/panels/sound/cc-speaker-test-button.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/sound/cc-speaker-test-button.c')
-rw-r--r--panels/sound/cc-speaker-test-button.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/panels/sound/cc-speaker-test-button.c b/panels/sound/cc-speaker-test-button.c
new file mode 100644
index 0000000..833b399
--- /dev/null
+++ b/panels/sound/cc-speaker-test-button.c
@@ -0,0 +1,243 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2018 Canonical Ltd.
+ *
+ * This program 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.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gsound.h>
+#include <pulse/pulseaudio.h>
+
+#include "cc-sound-resources.h"
+#include "cc-speaker-test-button.h"
+
+struct _CcSpeakerTestButton
+{
+ GtkDialog parent_instance;
+
+ GtkImage *image;
+ GtkLabel *label;
+
+ GCancellable *cancellable;
+ GSoundContext *context;
+ pa_channel_position_t position;
+ gboolean playing;
+ gint event_index;
+};
+
+G_DEFINE_TYPE (CcSpeakerTestButton, cc_speaker_test_button, GTK_TYPE_BUTTON)
+
+#define TEST_SOUND_ID 1
+
+static gboolean
+play_sound (CcSpeakerTestButton *self);
+
+static const gchar *
+get_icon_name (CcSpeakerTestButton *self)
+{
+ switch (self->position)
+ {
+ case PA_CHANNEL_POSITION_FRONT_LEFT:
+ return self->playing ? "audio-speaker-left-testing" : "audio-speaker-left";
+ case PA_CHANNEL_POSITION_FRONT_RIGHT:
+ return self->playing ? "audio-speaker-right-testing" : "audio-speaker-right";
+ case PA_CHANNEL_POSITION_FRONT_CENTER:
+ return self->playing ? "audio-speaker-center-testing" : "audio-speaker-center";
+ case PA_CHANNEL_POSITION_REAR_LEFT:
+ return self->playing ? "audio-speaker-left-back-testing" : "audio-speaker-left-back";
+ case PA_CHANNEL_POSITION_REAR_RIGHT:
+ return self->playing ? "audio-speaker-right-back-testing" : "audio-speaker-right-back";
+ case PA_CHANNEL_POSITION_REAR_CENTER:
+ return self->playing ? "audio-speaker-center-back-testing" : "audio-speaker-center-back";
+ case PA_CHANNEL_POSITION_LFE:
+ return self->playing ? "audio-subwoofer-testing" : "audio-subwoofer";
+ case PA_CHANNEL_POSITION_SIDE_LEFT:
+ return self->playing ? "audio-speaker-left-side-testing" : "audio-speaker-left-side";
+ case PA_CHANNEL_POSITION_SIDE_RIGHT:
+ return self->playing ? "audio-speaker-right-side-testing" : "audio-speaker-right-side";
+ case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+ return self->playing ? "audio-speaker-front-left-of-center-testing" : "audio-speaker-front-left-of-center";
+ case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+ return self->playing ? "audio-speaker-front-right-of-center-testing" : "audio-speaker-front-right-of-center";
+ case PA_CHANNEL_POSITION_MONO:
+ return self->playing ? "audio-speaker-mono-testing" : "audio-speaker-mono";
+ default:
+ return "audio-speakers";
+ }
+}
+
+static void
+update_icon (CcSpeakerTestButton *self)
+{
+ gtk_image_set_from_icon_name (self->image,
+ get_icon_name (self),
+ GTK_ICON_SIZE_DIALOG);
+}
+
+static GStrv
+get_sound_events (CcSpeakerTestButton *self)
+{
+ switch (self->position)
+ {
+ case PA_CHANNEL_POSITION_FRONT_LEFT:
+ return g_strsplit ("audio-channel-front-left;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_FRONT_RIGHT:
+ return g_strsplit ("audio-channel-front-right;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_FRONT_CENTER:
+ return g_strsplit ("audio-channel-front-center;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_REAR_LEFT:
+ return g_strsplit ("audio-channel-rear-left;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_REAR_RIGHT:
+ return g_strsplit ("audio-channel-rear-right;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_REAR_CENTER:
+ return g_strsplit ("audio-channel-rear-center;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_LFE:
+ return g_strsplit ("audio-channel-lfe;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_SIDE_LEFT:
+ return g_strsplit ("audio-channel-side-left;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_SIDE_RIGHT:
+ return g_strsplit ("audio-channel-side-right;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+ return g_strsplit ("audio-channel-front-left-of-center;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+ return g_strsplit ("audio-channel-front-right-of-center;audio-test-signal;bell", ";", -1);
+ case PA_CHANNEL_POSITION_MONO:
+ return g_strsplit ("audio-channel-mono;audio-test-signal;bell", ";", -1);
+ default:
+ return g_strsplit ("audio-test-signal;bell", ";", -1);
+ }
+}
+
+static void
+finish_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer userdata)
+{
+ CcSpeakerTestButton *self = userdata;
+ g_autoptr(GError) error = NULL;
+
+ if (!gsound_context_play_full_finish (GSOUND_CONTEXT (object), result, &error))
+ {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ if (play_sound (self))
+ return;
+
+ g_warning ("Failed to play sound: %s", error->message);
+ }
+
+ self->playing = FALSE;
+ update_icon (self);
+}
+
+static gboolean
+play_sound (CcSpeakerTestButton *self)
+{
+ g_auto(GStrv) events = NULL;
+
+ /* Stop existing sound */
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ self->cancellable = g_cancellable_new ();
+
+ events = get_sound_events (self);
+ if (events[self->event_index] == NULL)
+ return FALSE;
+
+ gsound_context_play_full (self->context, self->cancellable, finish_cb, self,
+ GSOUND_ATTR_MEDIA_ROLE, "test",
+ GSOUND_ATTR_MEDIA_NAME, pa_channel_position_to_pretty_string (self->position),
+ GSOUND_ATTR_CANBERRA_FORCE_CHANNEL, pa_channel_position_to_string (self->position),
+ GSOUND_ATTR_CANBERRA_ENABLE, "1",
+ GSOUND_ATTR_EVENT_ID, events[self->event_index],
+ NULL);
+ self->event_index++;
+
+ return TRUE;
+}
+
+static void
+clicked_cb (CcSpeakerTestButton *self)
+{
+ if (self->context == NULL)
+ return;
+
+ self->playing = TRUE;
+ update_icon (self);
+
+ /* Play the per-channel sound name or a generic sound */
+ self->event_index = 0;
+ play_sound (self);
+}
+
+static void
+cc_speaker_test_button_dispose (GObject *object)
+{
+ CcSpeakerTestButton *self = CC_SPEAKER_TEST_BUTTON (object);
+
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ g_clear_object (&self->context);
+
+ G_OBJECT_CLASS (cc_speaker_test_button_parent_class)->dispose (object);
+}
+
+void
+cc_speaker_test_button_class_init (CcSpeakerTestButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = cc_speaker_test_button_dispose;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/sound/cc-speaker-test-button.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcSpeakerTestButton, image);
+ gtk_widget_class_bind_template_child (widget_class, CcSpeakerTestButton, label);
+
+ gtk_widget_class_bind_template_callback (widget_class, clicked_cb);
+}
+
+void
+cc_speaker_test_button_init (CcSpeakerTestButton *self)
+{
+ g_resources_register (cc_sound_get_resource ());
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->cancellable = g_cancellable_new ();
+
+ update_icon (self);
+}
+
+CcSpeakerTestButton *
+cc_speaker_test_button_new (void)
+{
+ return g_object_new (CC_TYPE_SPEAKER_TEST_BUTTON, NULL);
+}
+
+void
+cc_speaker_test_button_set_channel_position (CcSpeakerTestButton *self,
+ GSoundContext *context,
+ pa_channel_position_t position)
+{
+ g_return_if_fail (CC_IS_SPEAKER_TEST_BUTTON (self));
+
+ g_clear_object (&self->context);
+ self->context = g_object_ref (context);
+ self->position = position;
+ gtk_label_set_label (self->label, pa_channel_position_to_pretty_string (position));
+ update_icon (self);
+}