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 |
|
|
|