2023-01-13 00:02:50 +01:00
|
|
|
/*
|
2018-10-10 16:08:37 +13:00
|
|
|
* 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>
|
|
|
|
|
2018-11-19 12:57:52 +13:00
|
|
|
#include "cc-sound-resources.h"
|
2018-10-10 16:08:37 +13:00
|
|
|
#include "cc-speaker-test-button.h"
|
|
|
|
|
|
|
|
struct _CcSpeakerTestButton
|
|
|
|
{
|
2022-12-16 15:52:05 +01:00
|
|
|
GtkButton parent_instance;
|
2018-10-10 16:08:37 +13:00
|
|
|
|
|
|
|
GCancellable *cancellable;
|
|
|
|
GSoundContext *context;
|
|
|
|
pa_channel_position_t position;
|
|
|
|
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:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-speaker-left";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_FRONT_RIGHT:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-speaker-right";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_FRONT_CENTER:
|
2022-12-16 15:52:05 +01:00
|
|
|
case PA_CHANNEL_POSITION_MONO:
|
|
|
|
return "audio-speaker-center";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_REAR_LEFT:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-speaker-left-back";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_REAR_RIGHT:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-speaker-right-back";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_REAR_CENTER:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-speaker-center-back";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_LFE:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-subwoofer";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_SIDE_LEFT:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-speaker-left-side";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_SIDE_RIGHT:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-speaker-right-side";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-speaker-front-left-of-center";
|
2018-10-10 16:08:37 +13:00
|
|
|
case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
|
2022-12-16 15:52:05 +01:00
|
|
|
return "audio-speaker-front-right-of-center";
|
2018-10-10 16:08:37 +13:00
|
|
|
default:
|
|
|
|
return "audio-speakers";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
update_icon (CcSpeakerTestButton *self)
|
|
|
|
{
|
2022-12-16 15:52:05 +01:00
|
|
|
gtk_button_set_icon_name (GTK_BUTTON (self), get_icon_name (self));
|
2018-10-10 16:08:37 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-12-16 15:52:05 +01:00
|
|
|
gtk_widget_remove_css_class (GTK_WIDGET (self), "playing");
|
2018-10-10 16:08:37 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2022-12-16 15:52:05 +01:00
|
|
|
gtk_widget_add_css_class (GTK_WIDGET (self), "playing");
|
2018-10-10 16:08:37 +13:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
object_class->dispose = cc_speaker_test_button_dispose;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cc_speaker_test_button_init (CcSpeakerTestButton *self)
|
|
|
|
{
|
|
|
|
self->cancellable = g_cancellable_new ();
|
|
|
|
|
|
|
|
update_icon (self);
|
2022-12-16 15:52:05 +01:00
|
|
|
|
|
|
|
g_signal_connect (self, "clicked", G_CALLBACK (clicked_cb), NULL);
|
2018-10-10 16:08:37 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
CcSpeakerTestButton *
|
2022-12-16 15:52:05 +01:00
|
|
|
cc_speaker_test_button_new (GSoundContext *context,
|
|
|
|
pa_channel_position_t position)
|
2018-10-10 16:08:37 +13:00
|
|
|
{
|
2022-12-16 15:52:05 +01:00
|
|
|
CcSpeakerTestButton *self = g_object_new (CC_TYPE_SPEAKER_TEST_BUTTON, NULL);
|
|
|
|
|
|
|
|
self->context = g_object_ref (context);
|
|
|
|
self->position = position;
|
|
|
|
update_icon (self);
|
2022-12-21 16:09:33 +01:00
|
|
|
gtk_accessible_update_property (GTK_ACCESSIBLE (self),
|
|
|
|
GTK_ACCESSIBLE_PROPERTY_LABEL,
|
|
|
|
pa_channel_position_to_pretty_string (position),
|
|
|
|
-1);
|
|
|
|
|
2022-12-16 15:52:05 +01:00
|
|
|
|
|
|
|
return self;
|
2018-10-10 16:08:37 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cc_speaker_test_button_set_channel_position (CcSpeakerTestButton *self,
|
|
|
|
pa_channel_position_t position)
|
|
|
|
{
|
|
|
|
g_return_if_fail (CC_IS_SPEAKER_TEST_BUTTON (self));
|
|
|
|
|
2022-12-16 15:52:05 +01:00
|
|
|
if (self->position == position)
|
|
|
|
return;
|
|
|
|
|
2018-10-10 16:08:37 +13:00
|
|
|
self->position = position;
|
|
|
|
update_icon (self);
|
|
|
|
}
|