Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / sys / oss4 / oss4-mixer-slider.c
1 /* GStreamer OSS4 mixer slider control
2  * Copyright (C) 2007-2008 Tim-Philipp Müller <tim centricular net>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /* A 'slider' in gnome-volume-control / GstMixer is represented by a
21  * GstMixerTrack with one or more channels.
22  *
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
25  * expect).
26  *
27  * From mixertrack.h:
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
30  * obvious."
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <sys/ioctl.h>
44
45 #include <gst/gst-i18n-plugin.h>
46
47 #define NO_LEGACY_MIXER
48 #include "oss4-mixer-slider.h"
49
50 GST_DEBUG_CATEGORY_EXTERN (oss4mixer_debug);
51 #define GST_CAT_DEFAULT oss4mixer_debug
52
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);
55
56 static void
57 gst_oss4_mixer_slider_class_init (GstOss4MixerSliderClass * klass)
58 {
59   /* nothing to do here */
60 }
61
62 static void
63 gst_oss4_mixer_slider_init (GstOss4MixerSlider * s)
64 {
65   /* nothing to do here */
66 }
67
68 static int
69 gst_oss4_mixer_slider_pack_volume (GstOss4MixerSlider * s, const gint * volumes)
70 {
71   int val = 0;
72
73   switch (s->mc->mixext.type) {
74     case MIXT_MONOSLIDER:
75     case MIXT_MONOSLIDER16:
76     case MIXT_SLIDER:
77       val = volumes[0];
78       break;
79     case MIXT_STEREOSLIDER:
80       val = ((volumes[1] & 0xff) << 8) | (volumes[0] & 0xff);
81       break;
82     case MIXT_STEREOSLIDER16:
83       val = ((volumes[1] & 0xffff) << 16) | (volumes[0] & 0xffff);
84       break;
85     default:
86       g_return_val_if_reached (0);
87   }
88   return val;
89 }
90
91 static void
92 gst_oss4_mixer_slider_unpack_volume (GstOss4MixerSlider * s, int v,
93     gint * volumes)
94 {
95   guint32 val;                  /* use uint so bitshifting the highest bit works right */
96
97   val = (guint32) v;
98   switch (s->mc->mixext.type) {
99     case MIXT_SLIDER:
100       volumes[0] = val;
101       break;
102     case MIXT_MONOSLIDER:
103       /* oss repeats the value in the upper bits, as if it was stereo */
104       volumes[0] = val & 0x00ff;
105       break;
106     case MIXT_MONOSLIDER16:
107       /* oss repeats the value in the upper bits, as if it was stereo */
108       volumes[0] = val & 0x0000ffff;
109       break;
110     case MIXT_STEREOSLIDER:
111       volumes[0] = (val & 0x00ff);
112       volumes[1] = (val & 0xff00) >> 8;
113       break;
114     case MIXT_STEREOSLIDER16:
115       volumes[0] = (val & 0x0000ffff);
116       volumes[1] = (val & 0xffff0000) >> 16;
117       break;
118     default:
119       g_return_if_reached ();
120   }
121 }
122
123 gboolean
124 gst_oss4_mixer_slider_get_volume (GstOss4MixerSlider * s, gint * volumes)
125 {
126   GstMixerTrack *track = GST_MIXER_TRACK (s);
127   int v = 0;
128
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];
136     return TRUE;
137   }
138
139   if (!gst_oss4_mixer_get_control_val (s->mixer, s->mc, &v))
140     return FALSE;
141
142   gst_oss4_mixer_slider_unpack_volume (s, v, volumes);
143
144   if (track->num_channels > 1) {
145     GST_LOG_OBJECT (s, "volume: left=%d, right=%d", volumes[0], volumes[1]);
146   } else {
147     GST_LOG_OBJECT (s, "volume: mono=%d", volumes[0]);
148   }
149
150   return TRUE;
151 }
152
153 gboolean
154 gst_oss4_mixer_slider_set_volume (GstOss4MixerSlider * s, const gint * volumes)
155 {
156   GstMixerTrack *track = GST_MIXER_TRACK (s);
157   int val = 0;
158
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)
164       goto done;
165   }
166
167   val = gst_oss4_mixer_slider_pack_volume (s, volumes);
168
169   if (track->num_channels > 1) {
170     GST_LOG_OBJECT (s, "left=%d, right=%d", volumes[0], volumes[1]);
171   } else {
172     GST_LOG_OBJECT (s, "mono=%d", volumes[0]);
173   }
174
175   if (!gst_oss4_mixer_set_control_val (s->mixer, s->mc, val))
176     return FALSE;
177
178 done:
179
180   s->volumes[0] = volumes[0];
181   if (track->num_channels == 2)
182     s->volumes[1] = volumes[1];
183
184   return TRUE;
185 }
186
187 gboolean
188 gst_oss4_mixer_slider_set_record (GstOss4MixerSlider * s, gboolean record)
189 {
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. */
192   return FALSE;
193 }
194
195 gboolean
196 gst_oss4_mixer_slider_set_mute (GstOss4MixerSlider * s, gboolean mute)
197 {
198   GstMixerTrack *track = GST_MIXER_TRACK (s);
199   gboolean ret;
200
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)) {
203     return TRUE;
204   }
205
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
209    * this control) */
210   if (s->mc->mute == NULL) {
211     int volume;
212
213     if (mute) {
214       /* make sure the current volume values get saved. */
215       gst_oss4_mixer_slider_get_volume (s, s->volumes);
216       volume = 0;
217     } else {
218       volume = gst_oss4_mixer_slider_pack_volume (s, s->volumes);
219     }
220     ret = gst_oss4_mixer_set_control_val (s->mixer, s->mc, volume);
221   } else {
222     ret = gst_oss4_mixer_set_control_val (s->mixer, s->mc->mute, ! !mute);
223   }
224
225   if (mute) {
226     track->flags |= GST_MIXER_TRACK_MUTE;
227   } else {
228     track->flags &= ~GST_MIXER_TRACK_MUTE;
229   }
230
231   return ret;
232 }
233
234 GstMixerTrack *
235 gst_oss4_mixer_slider_new (GstOss4Mixer * mixer, GstOss4MixerControl * mc)
236 {
237   GstOss4MixerSlider *s;
238   GstMixerTrack *track;
239   gint volumes[2] = { 0, };
240
241   s = g_object_new (GST_TYPE_OSS4_MIXER_SLIDER, "untranslated-label",
242       mc->mixext.extname, NULL);
243
244   track = GST_MIXER_TRACK (s);
245
246   /* caller will set track->label and track->flags */
247
248   s->mc = mc;
249   s->mixer = mixer;
250
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;
254
255   switch (mc->mixext.type) {
256     case MIXT_MONOSLIDER:
257     case MIXT_MONOSLIDER16:
258     case MIXT_SLIDER:
259       track->num_channels = 1;
260       break;
261     case MIXT_STEREOSLIDER:
262     case MIXT_STEREOSLIDER16:
263       track->num_channels = 2;
264       break;
265     default:
266       g_return_val_if_reached (NULL);
267   }
268
269   GST_LOG_OBJECT (track, "min=%d, max=%d, channels=%d", track->min_volume,
270       track->max_volume, track->num_channels);
271
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);
275     track = NULL;
276   }
277
278   return track;
279 }
280
281 /* This is called from the watch thread */
282 void
283 gst_oss4_mixer_slider_process_change_unlocked (GstMixerTrack * track)
284 {
285   GstOss4MixerSlider *s = GST_OSS4_MIXER_SLIDER_CAST (track);
286
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);
290   } else {
291     /* nothing to do here, since we don't/can't easily implement the record
292      * flag */
293   }
294
295   if (s->mc->changed) {
296     gint volumes[2] = { 0, 0 };
297
298     gst_oss4_mixer_slider_unpack_volume (s, s->mc->last_val, volumes);
299
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;
304       } else {
305         track->flags &= ~GST_MIXER_TRACK_MUTE;
306       }
307     }
308
309     gst_mixer_volume_changed (GST_MIXER (s->mixer), track, volumes);
310   }
311 }