1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5
Copyright (C) 2002, 2003 Bastien Nocera
6
Copyright (C) 2004 Novell, Inc.
7
Copyright (C) 2009 PERIER Romain <mrpouet@tuxfamily.org>
8
Copyright (C) 2011 Stefano Karapetsas <stefano@karapetsas.com>
10
The Mate Library is free software; you can redistribute it and/or
11
modify it under the terms of the GNU Library General Public License as
12
published by the Free Software Foundation; either version 2 of the
13
License, or (at your option) any later version.
15
The Mate Library is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
Library General Public License for more details.
20
You should have received a copy of the GNU Library General Public
21
License along with the Mate Library; see the file COPYING.LIB. If not,
22
write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23
Boston, MA 02110-1301, USA.
25
Author: Bastien Nocera <hadess@hadess.net>
26
Jon Trowbridge <trow@ximian.com>
30
#include "gvc-gstreamer-acme-vol.h"
33
#include <gst/audio/mixerutils.h>
34
#include <gst/interfaces/mixer.h>
35
#include <gst/interfaces/propertyprobe.h>
43
#define MATE_SOUND_SCHEMA "org.mate.sound"
44
#define DEFAULT_MIXER_DEVICE_KEY "default-mixer-device"
45
#define DEFAULT_MIXER_TRACKS_KEY "default-mixer-tracks"
47
#define ACME_VOLUME_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ACME_TYPE_VOLUME, AcmeVolumePrivate))
49
struct AcmeVolumePrivate {
58
G_DEFINE_TYPE (AcmeVolume, acme_volume, G_TYPE_OBJECT)
60
static gboolean acme_volume_open (AcmeVolume *acme);
61
static void acme_volume_close (AcmeVolume *acme);
62
static gboolean acme_volume_close_real (AcmeVolume *self);
64
static gpointer acme_volume_object = NULL;
67
acme_volume_finalize (GObject *object)
71
g_return_if_fail (object != NULL);
72
g_return_if_fail (ACME_IS_VOLUME (object));
74
self = ACME_VOLUME (object);
76
if (self->_priv->timer_id != 0)
77
g_source_remove (self->_priv->timer_id);
78
acme_volume_close_real (self);
80
if (self->_priv->settings != NULL) {
81
g_object_unref (self->_priv->settings);
82
self->_priv->settings = NULL;
85
G_OBJECT_CLASS (acme_volume_parent_class)->finalize (object);
89
acme_volume_set_mute (AcmeVolume *self, gboolean val)
93
g_return_if_fail(ACME_IS_VOLUME(self));
94
g_return_if_fail(acme_volume_open(self));
96
for (t = self->_priv->mixer_tracks; t != NULL; t = t->next) {
97
GstMixerTrack *track = GST_MIXER_TRACK (t->data);
98
gst_mixer_set_mute (self->_priv->mixer, track, val);
100
self->_priv->mute = val;
101
acme_volume_close (self);
105
update_state (AcmeVolume * self)
109
GstMixerTrack *track = GST_MIXER_TRACK (self->_priv->mixer_tracks->data);
111
/* update mixer by getting volume */
112
volumes = g_new0 (gint, track->num_channels);
113
gst_mixer_get_volume (self->_priv->mixer, track, volumes);
114
for (n = 0; n < track->num_channels; n++)
117
vol /= track->num_channels;
118
vol = 100 * vol / (track->max_volume - track->min_volume);
120
/* update mute flag, and volume if not muted */
121
if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE))
122
self->_priv->mute = TRUE;
123
self->_priv->volume = vol;
127
acme_volume_get_mute (AcmeVolume *self)
129
g_return_val_if_fail(acme_volume_open(self), FALSE);
132
acme_volume_close (self);
134
return self->_priv->mute;
138
acme_volume_get_volume (AcmeVolume *self)
141
g_return_val_if_fail(acme_volume_open(self), 0);
145
acme_volume_close (self);
147
return (gint) (self->_priv->volume + 0.5);
151
acme_volume_set_volume (AcmeVolume *self, gint val)
155
g_return_if_fail(acme_volume_open(self));
157
val = CLAMP (val, 0, 100);
159
for (t = self->_priv->mixer_tracks; t != NULL; t = t->next) {
160
GstMixerTrack *track = GST_MIXER_TRACK (t->data);
162
gdouble scale = (track->max_volume - track->min_volume) / 100.0;
163
gint vol = (gint) (val * scale + track->min_volume + 0.5);
165
volumes = g_new (gint, track->num_channels);
166
for (n = 0; n < track->num_channels; n++)
168
gst_mixer_set_volume (self->_priv->mixer, track, volumes);
173
self->_priv->volume = val;
175
acme_volume_close (self);
179
acme_volume_mute_toggle (AcmeVolume *self)
183
g_return_if_fail (self != NULL);
184
g_return_if_fail (ACME_IS_VOLUME(self));
186
muted = acme_volume_get_mute(self);
187
acme_volume_set_mute(self, !muted);
191
acme_volume_get_threshold (AcmeVolume *self)
196
g_return_val_if_fail(acme_volume_open(self), 1);
198
for (t = self->_priv->mixer_tracks; t != NULL; t = t->next) {
199
GstMixerTrack *track = GST_MIXER_TRACK (t->data);
200
gint track_steps = track->max_volume - track->min_volume;
201
if (track_steps > 0 && track_steps < steps)
205
acme_volume_close (self);
207
return 100 / steps + 1;
211
acme_volume_close_real (AcmeVolume *self)
213
if (self->_priv->mixer != NULL)
215
gst_element_set_state (GST_ELEMENT (self->_priv->mixer), GST_STATE_NULL);
216
gst_object_unref (GST_OBJECT (self->_priv->mixer));
217
g_list_foreach (self->_priv->mixer_tracks, (GFunc) g_object_unref, NULL);
218
g_list_free (self->_priv->mixer_tracks);
219
self->_priv->mixer = NULL;
220
self->_priv->mixer_tracks = NULL;
223
self->_priv->timer_id = 0;
229
* @mixer A pointer to mixer element
230
* @data A pointer to user data (AcmeVolume instance to be modified)
231
* @return A gboolean indicating success if Master track was found, failed otherwises.
234
_acme_set_mixer(GstMixer *mixer, gpointer user_data)
238
for (tracks = gst_mixer_list_tracks (mixer); tracks != NULL; tracks = tracks->next) {
239
GstMixerTrack *track = GST_MIXER_TRACK (tracks->data);
241
if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MASTER)) {
244
self = ACME_VOLUME (user_data);
246
self->_priv->mixer = mixer;
247
self->_priv->mixer_tracks = g_list_append (self->_priv->mixer_tracks, g_object_ref (track));
257
/* This is a modified version of code from gnome-media's gst-mixer */
259
acme_volume_open (AcmeVolume *self)
261
gchar *mixer_device, **factory_and_device = NULL;
264
if (self->_priv->timer_id != 0) {
265
g_source_remove (self->_priv->timer_id);
266
self->_priv->timer_id = 0;
270
mixer_device = g_settings_get_string (self->_priv->settings, DEFAULT_MIXER_DEVICE_KEY);
271
if (mixer_device != NULL)
272
factory_and_device = g_strsplit (mixer_device, ":", 2);
274
if (factory_and_device != NULL && factory_and_device[0] != NULL) {
277
element = gst_element_factory_make (factory_and_device[0], NULL);
279
if (element != NULL) {
280
if (factory_and_device[1] != NULL &&
281
g_object_class_find_property (G_OBJECT_GET_CLASS (element), "device"))
282
g_object_set (G_OBJECT (element), "device", factory_and_device[1], NULL);
283
gst_element_set_state (element, GST_STATE_READY);
285
if (GST_IS_MIXER (element))
286
self->_priv->mixer = GST_MIXER (element);
288
gst_element_set_state (element, GST_STATE_NULL);
289
gst_object_unref (element);
294
g_free (mixer_device);
295
g_strfreev (factory_and_device);
297
if (self->_priv->mixer != NULL) {
301
/* Try to use tracks saved in GSettings
302
Note: errors need to be treated , for example if the user set a non type list for this key
303
or if the elements type_list are not "matched" */
304
gchar **settings_list;
305
settings_list = g_settings_get_strv (self->_priv->settings, DEFAULT_MIXER_TRACKS_KEY);
306
if (settings_list != NULL) {
308
for (i = 0; i < G_N_ELEMENTS (settings_list); i++) {
309
if (settings_list[i] != NULL)
310
tracks = g_slist_append (tracks, g_strdup (settings_list[i]));
312
g_strfreev (settings_list);
315
/* We use these tracks ONLY if they are supported on the system with the following mixer */
316
for (m = gst_mixer_list_tracks (self->_priv->mixer); m != NULL; m = m->next) {
317
GstMixerTrack *track = GST_MIXER_TRACK (m->data);
319
for (t = tracks; t != NULL; t = t->next)
320
if (!strcmp (t->data, track->label))
321
self->_priv->mixer_tracks = g_list_append (self->_priv->mixer_tracks, g_object_ref (track));
325
g_slist_foreach (tracks, (GFunc)g_free, NULL);
326
g_slist_free (tracks);
328
/* If no track stored in GSettings is avaiable try to use Master track */
329
if (self->_priv->mixer_tracks == NULL) {
330
for (m = gst_mixer_list_tracks (self->_priv->mixer); m != NULL; m = m->next) {
331
GstMixerTrack *track = GST_MIXER_TRACK (m->data);
333
if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MASTER)) {
334
self->_priv->mixer_tracks = g_list_append (self->_priv->mixer_tracks, g_object_ref (track));
340
if (self->_priv->mixer_tracks != NULL)
343
gst_element_set_state (GST_ELEMENT (self->_priv->mixer), GST_STATE_NULL);
344
gst_object_unref (self->_priv->mixer);
348
/* Go through all elements of a certain class and check whether
349
* they implement a mixer. If so, walk through the tracks and look
350
* for first one named "volume".
352
* We should probably do something intelligent if we don't find an
353
* appropriate mixer/track. But now we do something stupid...
354
* everything just becomes a no-op.
356
mixer_list = gst_audio_default_registry_mixer_filter (_acme_set_mixer,
360
if (mixer_list == NULL)
363
/* do not unref the mixer as we keep the ref for self->priv->mixer */
364
g_list_free (mixer_list);
370
acme_volume_close (AcmeVolume *self)
372
self->_priv->timer_id = g_timeout_add_seconds (TIMEOUT,
373
(GSourceFunc) acme_volume_close_real, self);
377
acme_volume_init (AcmeVolume *self)
379
self->_priv = ACME_VOLUME_GET_PRIVATE (self);
380
self->_priv->settings = g_settings_new (MATE_SOUND_SCHEMA);
384
acme_volume_class_init (AcmeVolumeClass *klass)
386
G_OBJECT_CLASS (klass)->finalize = acme_volume_finalize;
388
gst_init (NULL, NULL);
390
g_type_class_add_private (klass, sizeof (AcmeVolumePrivate));
394
* @return A singleton instance of type AcmeVolume
397
acme_volume_new (void)
399
if (acme_volume_object == NULL) {
400
acme_volume_object = g_object_new (ACME_TYPE_VOLUME, NULL);
401
return ACME_VOLUME(acme_volume_object);
403
g_object_ref(acme_volume_object);
404
return ACME_VOLUME(acme_volume_object);