1 /* GStreamer OSS4 mixer slider control
2 * Copyright (C) 2007-2008 Tim-Philipp Müller <tim centricular net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 /* A 'slider' in gnome-volume-control / GstMixer is represented by a
21 * GstMixerTrack with one or more channels.
23 * A slider should be either flagged as INPUT or OUTPUT (mostly because of
24 * gnome-volume-control being littered with g_asserts for everything it doesn't
28 * "Input tracks can have 'recording' enabled, which means that any input will
29 * be hearable into the speakers that are attached to the output. Mute is
43 #include <sys/ioctl.h>
45 #include <gst/gst-i18n-plugin.h>
47 #define NO_LEGACY_MIXER
48 #include "oss4-mixer-slider.h"
50 GST_DEBUG_CATEGORY_EXTERN (oss4mixer_debug);
51 #define GST_CAT_DEFAULT oss4mixer_debug
53 /* GstMixerTrack is a plain GObject, so let's just use the GLib macro here */
54 G_DEFINE_TYPE (GstOss4MixerSlider, gst_oss4_mixer_slider, GST_TYPE_MIXER_TRACK);
57 gst_oss4_mixer_slider_class_init (GstOss4MixerSliderClass * klass)
59 /* nothing to do here */
63 gst_oss4_mixer_slider_init (GstOss4MixerSlider * s)
65 /* nothing to do here */
69 gst_oss4_mixer_slider_pack_volume (GstOss4MixerSlider * s, const gint * volumes)
73 switch (s->mc->mixext.type) {
75 case MIXT_MONOSLIDER16:
79 case MIXT_STEREOSLIDER:
80 val = ((volumes[1] & 0xff) << 8) | (volumes[0] & 0xff);
82 case MIXT_STEREOSLIDER16:
83 val = ((volumes[1] & 0xffff) << 16) | (volumes[0] & 0xffff);
86 g_return_val_if_reached (0);
92 gst_oss4_mixer_slider_unpack_volume (GstOss4MixerSlider * s, int v,
95 guint32 val; /* use uint so bitshifting the highest bit works right */
98 switch (s->mc->mixext.type) {
102 case MIXT_MONOSLIDER:
103 /* oss repeats the value in the upper bits, as if it was stereo */
104 volumes[0] = val & 0x00ff;
106 case MIXT_MONOSLIDER16:
107 /* oss repeats the value in the upper bits, as if it was stereo */
108 volumes[0] = val & 0x0000ffff;
110 case MIXT_STEREOSLIDER:
111 volumes[0] = (val & 0x00ff);
112 volumes[1] = (val & 0xff00) >> 8;
114 case MIXT_STEREOSLIDER16:
115 volumes[0] = (val & 0x0000ffff);
116 volumes[1] = (val & 0xffff0000) >> 16;
119 g_return_if_reached ();
124 gst_oss4_mixer_slider_get_volume (GstOss4MixerSlider * s, gint * volumes)
126 GstMixerTrack *track = GST_MIXER_TRACK (s);
129 /* if we're supposed to be muted, and don't have an actual mute control
130 * (ie. 'simulate' the mute), then just return the volume as saved, not
131 * the actually set volume which is most likely 0 */
132 if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE) && !s->mc->mute) {
133 volumes[0] = s->volumes[0];
134 if (track->num_channels == 2)
135 volumes[1] = s->volumes[1];
139 if (!gst_oss4_mixer_get_control_val (s->mixer, s->mc, &v))
142 gst_oss4_mixer_slider_unpack_volume (s, v, volumes);
144 if (track->num_channels > 1) {
145 GST_LOG_OBJECT (s, "volume: left=%d, right=%d", volumes[0], volumes[1]);
147 GST_LOG_OBJECT (s, "volume: mono=%d", volumes[0]);
154 gst_oss4_mixer_slider_set_volume (GstOss4MixerSlider * s, const gint * volumes)
156 GstMixerTrack *track = GST_MIXER_TRACK (s);
159 /* if we're supposed to be muted, and are 'simulating' the mute because
160 * we don't have a mute control, don't actually change the volume, just
161 * save it as the new desired volume for later when we get unmuted again */
162 if (!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_NO_MUTE)) {
163 if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE) && !s->mc->mute)
167 val = gst_oss4_mixer_slider_pack_volume (s, volumes);
169 if (track->num_channels > 1) {
170 GST_LOG_OBJECT (s, "left=%d, right=%d", volumes[0], volumes[1]);
172 GST_LOG_OBJECT (s, "mono=%d", volumes[0]);
175 if (!gst_oss4_mixer_set_control_val (s->mixer, s->mc, val))
180 s->volumes[0] = volumes[0];
181 if (track->num_channels == 2)
182 s->volumes[1] = volumes[1];
188 gst_oss4_mixer_slider_set_record (GstOss4MixerSlider * s, gboolean record)
190 /* There doesn't seem to be a way to do this using the OSS4 mixer API, so
191 * just do nothing here for now. */
196 gst_oss4_mixer_slider_set_mute (GstOss4MixerSlider * s, gboolean mute)
198 GstMixerTrack *track = GST_MIXER_TRACK (s);
201 /* if the control does not support muting, then do not do anything */
202 if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_NO_MUTE)) {
206 /* If we do not have a mute control, simulate mute (which is a bit broken,
207 * since we can not differentiate between capture/playback volume etc., so
208 * we just assume that setting the volume to 0 would be the same as muting
210 if (s->mc->mute == NULL) {
214 /* make sure the current volume values get saved. */
215 gst_oss4_mixer_slider_get_volume (s, s->volumes);
218 volume = gst_oss4_mixer_slider_pack_volume (s, s->volumes);
220 ret = gst_oss4_mixer_set_control_val (s->mixer, s->mc, volume);
222 ret = gst_oss4_mixer_set_control_val (s->mixer, s->mc->mute, !!mute);
226 track->flags |= GST_MIXER_TRACK_MUTE;
228 track->flags &= ~GST_MIXER_TRACK_MUTE;
235 gst_oss4_mixer_slider_new (GstOss4Mixer * mixer, GstOss4MixerControl * mc)
237 GstOss4MixerSlider *s;
238 GstMixerTrack *track;
239 gint volumes[2] = { 0, };
241 s = g_object_new (GST_TYPE_OSS4_MIXER_SLIDER, "untranslated-label",
242 mc->mixext.extname, NULL);
244 track = GST_MIXER_TRACK (s);
246 /* caller will set track->label and track->flags */
251 /* we don't do value scaling but just present a scale of 0-maxvalue */
252 track->min_volume = 0;
253 track->max_volume = mc->mixext.maxvalue;
255 switch (mc->mixext.type) {
256 case MIXT_MONOSLIDER:
257 case MIXT_MONOSLIDER16:
259 track->num_channels = 1;
261 case MIXT_STEREOSLIDER:
262 case MIXT_STEREOSLIDER16:
263 track->num_channels = 2;
266 g_return_val_if_reached (NULL);
269 GST_LOG_OBJECT (track, "min=%d, max=%d, channels=%d", track->min_volume,
270 track->max_volume, track->num_channels);
272 if (!gst_oss4_mixer_slider_get_volume (s, volumes)) {
273 GST_WARNING_OBJECT (track, "failed to read volume, returning NULL");
274 g_object_unref (track);
281 /* This is called from the watch thread */
283 gst_oss4_mixer_slider_process_change_unlocked (GstMixerTrack * track)
285 GstOss4MixerSlider *s = GST_OSS4_MIXER_SLIDER_CAST (track);
287 if (s->mc->mute != NULL && s->mc->mute->changed) {
288 gst_mixer_mute_toggled (GST_MIXER (s->mixer), track,
289 !!s->mc->mute->last_val);
291 /* nothing to do here, since we don't/can't easily implement the record
295 if (s->mc->changed) {
296 gint volumes[2] = { 0, 0 };
298 gst_oss4_mixer_slider_unpack_volume (s, s->mc->last_val, volumes);
300 /* if we 'simulate' the mute, update flag when the volume changes */
301 if (s->mc->mute == NULL) {
302 if (volumes[0] == 0 && volumes[1] == 0) {
303 track->flags |= GST_MIXER_TRACK_MUTE;
305 track->flags &= ~GST_MIXER_TRACK_MUTE;
309 gst_mixer_volume_changed (GST_MIXER (s->mixer), track, volumes);