GCC Code Coverage Report


Directory: ./
File: panels/sound/cc-speaker-test-button.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 115 0.0%
Functions: 0 14 0.0%
Branches: 0 54 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2018 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <gsound.h>
19 #include <pulse/pulseaudio.h>
20
21 #include "cc-sound-resources.h"
22 #include "cc-speaker-test-button.h"
23
24 struct _CcSpeakerTestButton
25 {
26 GtkButton parent_instance;
27
28 GCancellable *cancellable;
29 GSoundContext *context;
30 pa_channel_position_t position;
31 gint event_index;
32 };
33
34 G_DEFINE_TYPE (CcSpeakerTestButton, cc_speaker_test_button, GTK_TYPE_BUTTON)
35
36 #define TEST_SOUND_ID 1
37
38 static gboolean
39 play_sound (CcSpeakerTestButton *self);
40
41 static const gchar *
42 get_icon_name (CcSpeakerTestButton *self)
43 {
44 switch (self->position)
45 {
46 case PA_CHANNEL_POSITION_FRONT_LEFT:
47 return "audio-speaker-left";
48 case PA_CHANNEL_POSITION_FRONT_RIGHT:
49 return "audio-speaker-right";
50 case PA_CHANNEL_POSITION_FRONT_CENTER:
51 case PA_CHANNEL_POSITION_MONO:
52 return "audio-speaker-center";
53 case PA_CHANNEL_POSITION_REAR_LEFT:
54 return "audio-speaker-left-back";
55 case PA_CHANNEL_POSITION_REAR_RIGHT:
56 return "audio-speaker-right-back";
57 case PA_CHANNEL_POSITION_REAR_CENTER:
58 return "audio-speaker-center-back";
59 case PA_CHANNEL_POSITION_LFE:
60 return "audio-subwoofer";
61 case PA_CHANNEL_POSITION_SIDE_LEFT:
62 return "audio-speaker-left-side";
63 case PA_CHANNEL_POSITION_SIDE_RIGHT:
64 return "audio-speaker-right-side";
65 case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
66 return "audio-speaker-front-left-of-center";
67 case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
68 return "audio-speaker-front-right-of-center";
69 default:
70 return "audio-speakers";
71 }
72 }
73
74 static void
75 update_icon (CcSpeakerTestButton *self)
76 {
77 gtk_button_set_icon_name (GTK_BUTTON (self), get_icon_name (self));
78 }
79
80 static GStrv
81 get_sound_events (CcSpeakerTestButton *self)
82 {
83 switch (self->position)
84 {
85 case PA_CHANNEL_POSITION_FRONT_LEFT:
86 return g_strsplit ("audio-channel-front-left;audio-test-signal;bell", ";", -1);
87 case PA_CHANNEL_POSITION_FRONT_RIGHT:
88 return g_strsplit ("audio-channel-front-right;audio-test-signal;bell", ";", -1);
89 case PA_CHANNEL_POSITION_FRONT_CENTER:
90 return g_strsplit ("audio-channel-front-center;audio-test-signal;bell", ";", -1);
91 case PA_CHANNEL_POSITION_REAR_LEFT:
92 return g_strsplit ("audio-channel-rear-left;audio-test-signal;bell", ";", -1);
93 case PA_CHANNEL_POSITION_REAR_RIGHT:
94 return g_strsplit ("audio-channel-rear-right;audio-test-signal;bell", ";", -1);
95 case PA_CHANNEL_POSITION_REAR_CENTER:
96 return g_strsplit ("audio-channel-rear-center;audio-test-signal;bell", ";", -1);
97 case PA_CHANNEL_POSITION_LFE:
98 return g_strsplit ("audio-channel-lfe;audio-test-signal;bell", ";", -1);
99 case PA_CHANNEL_POSITION_SIDE_LEFT:
100 return g_strsplit ("audio-channel-side-left;audio-test-signal;bell", ";", -1);
101 case PA_CHANNEL_POSITION_SIDE_RIGHT:
102 return g_strsplit ("audio-channel-side-right;audio-test-signal;bell", ";", -1);
103 case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
104 return g_strsplit ("audio-channel-front-left-of-center;audio-test-signal;bell", ";", -1);
105 case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
106 return g_strsplit ("audio-channel-front-right-of-center;audio-test-signal;bell", ";", -1);
107 case PA_CHANNEL_POSITION_MONO:
108 return g_strsplit ("audio-channel-mono;audio-test-signal;bell", ";", -1);
109 default:
110 return g_strsplit ("audio-test-signal;bell", ";", -1);
111 }
112 }
113
114 static void
115 finish_cb (GObject *object,
116 GAsyncResult *result,
117 gpointer userdata)
118 {
119 CcSpeakerTestButton *self = userdata;
120 g_autoptr(GError) error = NULL;
121
122 if (!gsound_context_play_full_finish (GSOUND_CONTEXT (object), result, &error))
123 {
124 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
125 return;
126
127 if (play_sound (self))
128 return;
129
130 g_warning ("Failed to play sound: %s", error->message);
131 }
132
133 gtk_widget_remove_css_class (GTK_WIDGET (self), "playing");
134 }
135
136 static gboolean
137 play_sound (CcSpeakerTestButton *self)
138 {
139 g_auto(GStrv) events = NULL;
140
141 /* Stop existing sound */
142 g_cancellable_cancel (self->cancellable);
143 g_clear_object (&self->cancellable);
144 self->cancellable = g_cancellable_new ();
145
146 events = get_sound_events (self);
147 if (events[self->event_index] == NULL)
148 return FALSE;
149
150 gsound_context_play_full (self->context, self->cancellable, finish_cb, self,
151 GSOUND_ATTR_MEDIA_ROLE, "test",
152 GSOUND_ATTR_MEDIA_NAME, pa_channel_position_to_pretty_string (self->position),
153 GSOUND_ATTR_CANBERRA_FORCE_CHANNEL, pa_channel_position_to_string (self->position),
154 GSOUND_ATTR_CANBERRA_ENABLE, "1",
155 GSOUND_ATTR_EVENT_ID, events[self->event_index],
156 NULL);
157 self->event_index++;
158
159 return TRUE;
160 }
161
162 static void
163 clicked_cb (CcSpeakerTestButton *self)
164 {
165 if (self->context == NULL)
166 return;
167
168 gtk_widget_add_css_class (GTK_WIDGET (self), "playing");
169
170 /* Play the per-channel sound name or a generic sound */
171 self->event_index = 0;
172 play_sound (self);
173 }
174
175 static void
176 cc_speaker_test_button_dispose (GObject *object)
177 {
178 CcSpeakerTestButton *self = CC_SPEAKER_TEST_BUTTON (object);
179
180 g_cancellable_cancel (self->cancellable);
181 g_clear_object (&self->cancellable);
182 g_clear_object (&self->context);
183
184 G_OBJECT_CLASS (cc_speaker_test_button_parent_class)->dispose (object);
185 }
186
187 void
188 cc_speaker_test_button_class_init (CcSpeakerTestButtonClass *klass)
189 {
190 GObjectClass *object_class = G_OBJECT_CLASS (klass);
191
192 object_class->dispose = cc_speaker_test_button_dispose;
193 }
194
195 void
196 cc_speaker_test_button_init (CcSpeakerTestButton *self)
197 {
198 self->cancellable = g_cancellable_new ();
199
200 update_icon (self);
201
202 g_signal_connect (self, "clicked", G_CALLBACK (clicked_cb), NULL);
203 }
204
205 GtkWidget *
206 cc_speaker_test_button_new (GSoundContext *context,
207 pa_channel_position_t position)
208 {
209 CcSpeakerTestButton *self = g_object_new (CC_TYPE_SPEAKER_TEST_BUTTON, NULL);
210
211 self->context = g_object_ref (context);
212 self->position = position;
213 update_icon (self);
214
215 gtk_widget_set_tooltip_text (GTK_WIDGET (self),
216 pa_channel_position_to_pretty_string (position));
217
218 return GTK_WIDGET (self);
219 }
220
221 void
222 cc_speaker_test_button_set_channel_position (CcSpeakerTestButton *self,
223 pa_channel_position_t position)
224 {
225 g_return_if_fail (CC_IS_SPEAKER_TEST_BUTTON (self));
226
227 if (self->position == position)
228 return;
229
230 self->position = position;
231 update_icon (self);
232 }
233