1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2009 Bastien Nocera
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28
#include <glib/gi18n-lib.h>
31
#include <canberra-gtk.h>
32
#include <pulse/pulseaudio.h>
34
#include "gvc-speaker-test.h"
35
#include "gvc-mixer-stream.h"
36
#include "gvc-mixer-card.h"
38
#define GVC_SPEAKER_TEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_SPEAKER_TEST, GvcSpeakerTestPrivate))
40
struct GvcSpeakerTestPrivate
42
GtkWidget *channel_controls[PA_CHANNEL_POSITION_MAX];
45
GvcMixerControl *control;
60
static void gvc_speaker_test_class_init (GvcSpeakerTestClass *klass);
61
static void gvc_speaker_test_init (GvcSpeakerTest *speaker_test);
62
static void gvc_speaker_test_finalize (GObject *object);
63
static void update_channel_map (GvcSpeakerTest *speaker_test);
65
G_DEFINE_TYPE (GvcSpeakerTest, gvc_speaker_test, GTK_TYPE_TABLE)
67
static const int position_table[] = {
69
PA_CHANNEL_POSITION_FRONT_LEFT, 0, 0,
70
PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, 1, 0,
71
PA_CHANNEL_POSITION_FRONT_CENTER, 2, 0,
72
PA_CHANNEL_POSITION_MONO, 2, 0,
73
PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, 3, 0,
74
PA_CHANNEL_POSITION_FRONT_RIGHT, 4, 0,
75
PA_CHANNEL_POSITION_SIDE_LEFT, 0, 1,
76
PA_CHANNEL_POSITION_SIDE_RIGHT, 4, 1,
77
PA_CHANNEL_POSITION_REAR_LEFT, 0, 2,
78
PA_CHANNEL_POSITION_REAR_CENTER, 2, 2,
79
PA_CHANNEL_POSITION_REAR_RIGHT, 4, 2,
80
PA_CHANNEL_POSITION_LFE, 3, 2
84
gvc_speaker_test_set_property (GObject *object,
89
GvcSpeakerTest *self = GVC_SPEAKER_TEST (object);
93
self->priv->card = g_value_dup_object (value);
94
if (self->priv->control != NULL)
95
update_channel_map (self);
98
self->priv->control = g_value_dup_object (value);
99
if (self->priv->card != NULL)
100
update_channel_map (self);
103
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
109
gvc_speaker_test_get_property (GObject *object,
114
GvcSpeakerTest *self = GVC_SPEAKER_TEST (object);
118
g_value_set_object (value, self->priv->card);
121
g_value_set_object (value, self->priv->control);
124
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130
gvc_speaker_test_class_init (GvcSpeakerTestClass *klass)
132
GObjectClass *object_class = G_OBJECT_CLASS (klass);
134
object_class->finalize = gvc_speaker_test_finalize;
135
object_class->set_property = gvc_speaker_test_set_property;
136
object_class->get_property = gvc_speaker_test_get_property;
138
g_object_class_install_property (object_class,
140
g_param_spec_object ("card",
144
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
145
g_object_class_install_property (object_class,
147
g_param_spec_object ("control",
149
"The mixer controller",
150
GVC_TYPE_MIXER_CONTROL,
151
G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
152
g_type_class_add_private (klass, sizeof (GvcSpeakerTestPrivate));
156
sound_name (pa_channel_position_t position)
159
case PA_CHANNEL_POSITION_FRONT_LEFT:
160
return "audio-channel-front-left";
161
case PA_CHANNEL_POSITION_FRONT_RIGHT:
162
return "audio-channel-front-right";
163
case PA_CHANNEL_POSITION_FRONT_CENTER:
164
return "audio-channel-front-center";
165
case PA_CHANNEL_POSITION_REAR_LEFT:
166
return "audio-channel-rear-left";
167
case PA_CHANNEL_POSITION_REAR_RIGHT:
168
return "audio-channel-rear-right";
169
case PA_CHANNEL_POSITION_REAR_CENTER:
170
return "audio-channel-rear-center";
171
case PA_CHANNEL_POSITION_LFE:
172
return "audio-channel-lfe";
173
case PA_CHANNEL_POSITION_SIDE_LEFT:
174
return "audio-channel-side-left";
175
case PA_CHANNEL_POSITION_SIDE_RIGHT:
176
return "audio-channel-side-right";
183
icon_name (pa_channel_position_t position, gboolean playing)
186
case PA_CHANNEL_POSITION_FRONT_LEFT:
187
return playing ? "audio-speaker-left-testing" : "audio-speaker-left";
188
case PA_CHANNEL_POSITION_FRONT_RIGHT:
189
return playing ? "audio-speaker-right-testing" : "audio-speaker-right";
190
case PA_CHANNEL_POSITION_FRONT_CENTER:
191
return playing ? "audio-speaker-center-testing" : "audio-speaker-center";
192
case PA_CHANNEL_POSITION_REAR_LEFT:
193
return playing ? "audio-speaker-left-back-testing" : "audio-speaker-left-back";
194
case PA_CHANNEL_POSITION_REAR_RIGHT:
195
return playing ? "audio-speaker-right-back-testing" : "audio-speaker-right-back";
196
case PA_CHANNEL_POSITION_REAR_CENTER:
197
return playing ? "audio-speaker-center-back-testing" : "audio-speaker-center-back";
198
case PA_CHANNEL_POSITION_LFE:
199
return playing ? "audio-subwoofer-testing" : "audio-subwoofer";
200
case PA_CHANNEL_POSITION_SIDE_LEFT:
201
return playing ? "audio-speaker-left-side-testing" : "audio-speaker-left-side";
202
case PA_CHANNEL_POSITION_SIDE_RIGHT:
203
return playing ? "audio-speaker-right-side-testing" : "audio-speaker-right-side";
210
update_button (GtkWidget *control)
214
pa_channel_position_t position;
217
button = g_object_get_data (G_OBJECT (control), "button");
218
image = g_object_get_data (G_OBJECT (control), "image");
219
position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "position"));
220
playing = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "playing"));
221
gtk_button_set_label (GTK_BUTTON (button), playing ? _("Stop") : _("Test"));
222
gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name (position, playing), GTK_ICON_SIZE_DIALOG);
226
pretty_position (pa_channel_position_t position)
228
if (position == PA_CHANNEL_POSITION_LFE)
229
return N_("Subwoofer");
231
return pa_channel_position_to_pretty_string (position);
235
idle_cb (GtkWidget *control)
240
/* This is called in the background thread, hence
241
* forward to main thread via idle callback */
242
g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER(FALSE));
243
update_button (control);
249
finish_cb (ca_context *c, uint32_t id, int error_code, void *userdata)
251
GtkWidget *control = (GtkWidget *) userdata;
253
if (error_code == CA_ERROR_DESTROYED || control == NULL)
255
g_idle_add ((GSourceFunc) idle_cb, control);
259
on_test_button_clicked (GtkButton *button,
263
ca_context *canberra;
265
canberra = g_object_get_data (G_OBJECT (control), "canberra");
267
ca_context_cancel (canberra, 1);
269
playing = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "playing"));
272
g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER(FALSE));
274
pa_channel_position_t position;
276
ca_proplist *proplist;
278
position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (control), "position"));
280
ca_proplist_create (&proplist);
281
ca_proplist_sets (proplist, CA_PROP_MEDIA_ROLE, "test");
282
ca_proplist_sets (proplist, CA_PROP_MEDIA_NAME, pretty_position (position));
283
ca_proplist_sets (proplist, CA_PROP_CANBERRA_FORCE_CHANNEL,
284
pa_channel_position_to_string (position));
285
ca_proplist_sets (proplist, CA_PROP_CANBERRA_ENABLE, "1");
287
name = sound_name (position);
289
ca_proplist_sets (proplist, CA_PROP_EVENT_ID, name);
290
playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0;
294
ca_proplist_sets (proplist, CA_PROP_EVENT_ID, "audio-test-signal");
295
playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0;
299
ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "bell-window-system");
300
playing = ca_context_play_full (canberra, 1, proplist, finish_cb, control) >= 0;
302
g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER(playing));
305
update_button (control);
309
channel_control_new (ca_context *canberra, pa_channel_position_t position)
315
GtkWidget *test_button;
318
control = gtk_vbox_new (FALSE, 6);
319
g_object_set_data (G_OBJECT (control), "playing", GINT_TO_POINTER(FALSE));
320
g_object_set_data (G_OBJECT (control), "position", GINT_TO_POINTER(position));
321
g_object_set_data (G_OBJECT (control), "canberra", canberra);
323
name = icon_name (position, FALSE);
325
name = "audio-volume-medium";
326
image = gtk_image_new_from_icon_name (name, GTK_ICON_SIZE_DIALOG);
327
g_object_set_data (G_OBJECT (control), "image", image);
328
gtk_box_pack_start (GTK_BOX (control), image, FALSE, FALSE, 0);
330
label = gtk_label_new (pretty_position (position));
331
gtk_box_pack_start (GTK_BOX (control), label, FALSE, FALSE, 0);
333
test_button = gtk_button_new_with_label (_("Test"));
334
g_signal_connect (G_OBJECT (test_button), "clicked",
335
G_CALLBACK (on_test_button_clicked), control);
336
g_object_set_data (G_OBJECT (control), "button", test_button);
338
box = gtk_hbox_new (FALSE, 0);
339
gtk_box_pack_start (GTK_BOX (box), test_button, TRUE, FALSE, 0);
340
gtk_box_pack_start (GTK_BOX (control), box, FALSE, FALSE, 0);
342
gtk_widget_show_all (control);
348
create_channel_controls (GvcSpeakerTest *speaker_test)
352
for (i = 0; i < G_N_ELEMENTS (position_table); i += 3) {
353
speaker_test->priv->channel_controls[position_table[i]] = channel_control_new (speaker_test->priv->canberra, (pa_channel_position_t) position_table[i]);
354
gtk_table_attach (GTK_TABLE (speaker_test),
355
speaker_test->priv->channel_controls[position_table[i]],
357
position_table[i+1]+1,
359
position_table[i+2]+1,
360
GTK_EXPAND, GTK_EXPAND, 0, 0);
364
static const GvcChannelMap *
365
get_channel_map_for_card (GvcMixerControl *control,
371
GvcMixerStream *stream;
372
const GvcChannelMap *map;
374
/* This gets the channel map for the only
375
* output for the card */
377
card_index = gvc_mixer_card_get_index (card);
378
if (card_index == PA_INVALID_INDEX)
380
sinks = gvc_mixer_control_get_sinks (control);
382
for (l = sinks; l != NULL; l = l->next) {
383
GvcMixerStream *s = l->data;
384
if (gvc_mixer_stream_get_card_index (s) == card_index) {
385
stream = g_object_ref (s);
389
g_slist_free (sinks);
393
g_debug ("Found stream '%s' for card '%s'",
394
gvc_mixer_stream_get_name (stream),
395
gvc_mixer_card_get_name (card));
397
*output_name = g_strdup (gvc_mixer_stream_get_name (stream));
398
map = gvc_mixer_stream_get_channel_map (stream);
400
g_debug ("Got channel map '%s' for port '%s'",
401
gvc_channel_map_get_mapping (map), *output_name);
407
update_channel_map (GvcSpeakerTest *speaker_test)
410
const GvcChannelMap *map;
413
g_return_if_fail (speaker_test->priv->control != NULL);
414
g_return_if_fail (speaker_test->priv->card != NULL);
416
g_debug ("XXX update_channel_map called XXX");
418
map = get_channel_map_for_card (speaker_test->priv->control,
419
speaker_test->priv->card,
422
g_return_if_fail (map != NULL);
424
ca_context_change_device (speaker_test->priv->canberra, output_name);
425
g_free (output_name);
427
for (i = 0; i < G_N_ELEMENTS (position_table); i += 3) {
428
gtk_widget_set_visible (speaker_test->priv->channel_controls[position_table[i]],
429
gvc_channel_map_has_position(map, position_table[i]));
434
gvc_speaker_test_init (GvcSpeakerTest *speaker_test)
438
speaker_test->priv = GVC_SPEAKER_TEST_GET_PRIVATE (speaker_test);
440
ca_context_create (&speaker_test->priv->canberra);
441
ca_context_set_driver (speaker_test->priv->canberra, "pulse");
442
ca_context_change_props (speaker_test->priv->canberra,
443
CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl",
446
gtk_table_resize (GTK_TABLE (speaker_test), 3, 5);
447
gtk_container_set_border_width (GTK_CONTAINER (speaker_test), 12);
448
gtk_table_set_homogeneous (GTK_TABLE (speaker_test), TRUE);
449
gtk_table_set_row_spacings (GTK_TABLE (speaker_test), 12);
450
gtk_table_set_col_spacings (GTK_TABLE (speaker_test), 12);
452
create_channel_controls (speaker_test);
454
face = gtk_image_new_from_icon_name ("face-smile", GTK_ICON_SIZE_DIALOG);
455
gtk_table_attach (GTK_TABLE (speaker_test), face,
456
2, 3, 1, 2, GTK_EXPAND, GTK_EXPAND, 0, 0);
457
gtk_widget_show (face);
461
gvc_speaker_test_finalize (GObject *object)
463
GvcSpeakerTest *speaker_test;
465
g_return_if_fail (object != NULL);
466
g_return_if_fail (GVC_IS_SPEAKER_TEST (object));
468
speaker_test = GVC_SPEAKER_TEST (object);
470
g_return_if_fail (speaker_test->priv != NULL);
472
g_object_unref (speaker_test->priv->card);
473
speaker_test->priv->card = NULL;
475
g_object_unref (speaker_test->priv->control);
476
speaker_test->priv->control = NULL;
478
ca_context_destroy (speaker_test->priv->canberra);
479
speaker_test->priv->canberra = NULL;
481
G_OBJECT_CLASS (gvc_speaker_test_parent_class)->finalize (object);
485
gvc_speaker_test_new (GvcMixerControl *control,
488
GObject *speaker_test;
490
g_return_val_if_fail (card != NULL, NULL);
491
g_return_val_if_fail (control != NULL, NULL);
493
speaker_test = g_object_new (GVC_TYPE_SPEAKER_TEST,
498
return GTK_WIDGET (speaker_test);