sys/oss/gstossmixerelement.*: Added mixer element like alsamixer.
[platform/upstream/gstreamer.git] / sys / oss / gstossmixer.c
1 /* GStreamer OSS Mixer implementation
2  * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  *
4  * gstossmixer.c: mixer interface implementation for OSS
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <sys/ioctl.h>
33 #include <sys/soundcard.h>
34
35 #include <gst/gst-i18n-plugin.h>
36
37 #include "gstossmixer.h"
38 #include "gstossmixertrack.h"
39
40 #define MASK_BIT_IS_SET(mask, bit) \
41   (mask & (1 << bit))
42
43 static gboolean
44 gst_ossmixer_open (GstOssMixer * mixer)
45 {
46 #ifdef SOUND_MIXER_INFO
47   struct mixer_info minfo;
48 #endif
49
50   g_return_val_if_fail (mixer->mixer_fd == -1, FALSE);
51
52   mixer->mixer_fd = open (mixer->device, O_RDWR);
53   if (mixer->mixer_fd == -1) {
54     /* this is valid. OSS devices don't need to expose a mixer */
55     GST_DEBUG ("Failed to open mixer device %s, mixing disabled: %s",
56         mixer->device, strerror (errno));
57     return FALSE;
58   }
59
60   /* get masks */
61   if (ioctl (mixer->mixer_fd, SOUND_MIXER_READ_RECMASK, &mixer->recmask) < 0
62       || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_RECSRC, &mixer->recdevs) < 0
63       || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_STEREODEVS,
64           &mixer->stereomask) < 0
65       || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_DEVMASK, &mixer->devmask) < 0
66       || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_CAPS, &mixer->mixcaps) < 0) {
67     GST_DEBUG ("Failed to get device masks");
68     close (mixer->mixer_fd);
69     mixer->mixer_fd = -1;
70     return FALSE;
71   }
72
73   /* get name */
74 #ifdef SOUND_MIXER_INFO
75   if (ioctl (mixer->mixer_fd, SOUND_MIXER_INFO, &minfo) == 0) {
76     mixer->cardname = g_strdup (minfo.name);
77   }
78 #else
79   oss->cardname = g_strdup ("Unknown");
80 #endif
81
82   return TRUE;
83 }
84
85 static void
86 gst_ossmixer_ensure_track_list (GstOssMixer * mixer)
87 {
88   gint i, master = -1;
89
90   g_return_if_fail (mixer->mixer_fd != -1);
91
92   if (mixer->tracklist)
93     return;
94
95   /* find master volume */
96   if (mixer->devmask & SOUND_MASK_VOLUME)
97     master = SOUND_MIXER_VOLUME;
98   else if (mixer->devmask & SOUND_MASK_PCM)
99     master = SOUND_MIXER_PCM;
100   else if (mixer->devmask & SOUND_MASK_SPEAKER)
101     master = SOUND_MIXER_SPEAKER;       /* doubtful... */
102   /* else: no master, so we won't set any */
103
104   /* build track list */
105   for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
106     if (mixer->devmask & (1 << i)) {
107       GstMixerTrack *track;
108       gboolean input = FALSE, stereo = FALSE, record = FALSE;
109
110       /* track exists, make up capabilities */
111       if (MASK_BIT_IS_SET (mixer->stereomask, i))
112         stereo = TRUE;
113       if (MASK_BIT_IS_SET (mixer->recmask, i))
114         input = TRUE;
115       if (MASK_BIT_IS_SET (mixer->recdevs, i))
116         record = TRUE;
117
118       /* do we want this in our list? */
119       if (!((mixer->dir & GST_OSS_MIXER_CAPTURE && input == TRUE) ||
120               (mixer->dir & GST_OSS_MIXER_PLAYBACK && i != SOUND_MIXER_PCM)))
121         /* the PLAYBACK case seems hacky, but that's how 0.8 had it */
122         continue;
123
124       /* add track to list */
125       track = gst_ossmixer_track_new (mixer->mixer_fd, i, stereo ? 2 : 1,
126           (record ? GST_MIXER_TRACK_RECORD : 0) |
127           (input ? GST_MIXER_TRACK_INPUT :
128               GST_MIXER_TRACK_OUTPUT) |
129           ((master != i) ? 0 : GST_MIXER_TRACK_MASTER));
130       mixer->tracklist = g_list_append (mixer->tracklist, track);
131     }
132   }
133 }
134
135 GstOssMixer *
136 gst_ossmixer_new (const char *device, GstOssMixerDirection dir)
137 {
138   GstOssMixer *ret = NULL;
139
140   g_return_val_if_fail (device != NULL, NULL);
141
142   ret = g_new0 (GstOssMixer, 1);
143
144   ret->device = g_strdup (device);
145   ret->dir = dir;
146   ret->mixer_fd = -1;
147
148   if (!gst_ossmixer_open (ret))
149     goto error;
150
151   return ret;
152
153 error:
154   if (ret)
155     gst_ossmixer_free (ret);
156
157   return NULL;
158 }
159
160 void
161 gst_ossmixer_free (GstOssMixer * mixer)
162 {
163   g_return_if_fail (mixer != NULL);
164
165   if (mixer->device) {
166     g_free (mixer->device);
167     mixer->device = NULL;
168   }
169
170   if (mixer->cardname) {
171     g_free (mixer->cardname);
172     mixer->cardname = NULL;
173   }
174
175   if (mixer->tracklist) {
176     g_list_foreach (mixer->tracklist, (GFunc) g_object_unref, NULL);
177     g_list_free (mixer->tracklist);
178     mixer->tracklist = NULL;
179   }
180
181   if (mixer->mixer_fd != -1) {
182     close (mixer->mixer_fd);
183     mixer->mixer_fd = -1;
184   }
185
186   g_free (mixer);
187 }
188
189 /* unused with G_DISABLE_* */
190 static G_GNUC_UNUSED gboolean
191 gst_ossmixer_contains_track (GstOssMixer * mixer, GstOssMixerTrack * osstrack)
192 {
193   const GList *item;
194
195   for (item = mixer->tracklist; item != NULL; item = item->next)
196     if (item->data == osstrack)
197       return TRUE;
198
199   return FALSE;
200 }
201
202 const GList *
203 gst_ossmixer_list_tracks (GstOssMixer * mixer)
204 {
205   gst_ossmixer_ensure_track_list (mixer);
206
207   return (const GList *) mixer->tracklist;
208 }
209
210 void
211 gst_ossmixer_get_volume (GstOssMixer * mixer,
212     GstMixerTrack * track, gint * volumes)
213 {
214   gint volume;
215   GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
216
217   g_return_if_fail (mixer->mixer_fd != -1);
218   g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
219
220   if (track->flags & GST_MIXER_TRACK_MUTE) {
221     volumes[0] = osstrack->lvol;
222     if (track->num_channels == 2) {
223       volumes[1] = osstrack->rvol;
224     }
225   } else {
226     /* get */
227     if (ioctl (mixer->mixer_fd, MIXER_READ (osstrack->track_num), &volume) < 0) {
228       g_warning ("Error getting recording device (%d) volume: %s",
229           osstrack->track_num, strerror (errno));
230       volume = 0;
231     }
232
233     osstrack->lvol = volumes[0] = (volume & 0xff);
234     if (track->num_channels == 2) {
235       osstrack->rvol = volumes[1] = ((volume >> 8) & 0xff);
236     }
237   }
238 }
239
240 void
241 gst_ossmixer_set_volume (GstOssMixer * mixer,
242     GstMixerTrack * track, gint * volumes)
243 {
244   gint volume;
245   GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
246
247   g_return_if_fail (mixer->mixer_fd != -1);
248   g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
249
250   /* prepare the value for ioctl() */
251   if (!(track->flags & GST_MIXER_TRACK_MUTE)) {
252     volume = (volumes[0] & 0xff);
253     if (track->num_channels == 2) {
254       volume |= ((volumes[1] & 0xff) << 8);
255     }
256
257     /* set */
258     if (ioctl (mixer->mixer_fd, MIXER_WRITE (osstrack->track_num), &volume) < 0) {
259       g_warning ("Error setting recording device (%d) volume (0x%x): %s",
260           osstrack->track_num, volume, strerror (errno));
261       return;
262     }
263   }
264
265   osstrack->lvol = volumes[0];
266   if (track->num_channels == 2) {
267     osstrack->rvol = volumes[1];
268   }
269 }
270
271 void
272 gst_ossmixer_set_mute (GstOssMixer * mixer, GstMixerTrack * track,
273     gboolean mute)
274 {
275   int volume;
276   GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
277
278   g_return_if_fail (mixer->mixer_fd != -1);
279   g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
280
281   if (mute) {
282     volume = 0;
283   } else {
284     volume = (osstrack->lvol & 0xff);
285     if (MASK_BIT_IS_SET (mixer->stereomask, osstrack->track_num)) {
286       volume |= ((osstrack->rvol & 0xff) << 8);
287     }
288   }
289
290   if (ioctl (mixer->mixer_fd, MIXER_WRITE (osstrack->track_num), &volume) < 0) {
291     g_warning ("Error setting mixer recording device volume (0x%x): %s",
292         volume, strerror (errno));
293     return;
294   }
295
296   if (mute) {
297     track->flags |= GST_MIXER_TRACK_MUTE;
298   } else {
299     track->flags &= ~GST_MIXER_TRACK_MUTE;
300   }
301 }
302
303 void
304 gst_ossmixer_set_record (GstOssMixer * mixer,
305     GstMixerTrack * track, gboolean record)
306 {
307   GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
308
309   g_return_if_fail (mixer->mixer_fd != -1);
310   g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
311
312   /* if there's nothing to do... */
313   if ((record && GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)) ||
314       (!record && !GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)))
315     return;
316
317   /* if we're exclusive, then we need to unset the current one(s) */
318   if (mixer->mixcaps & SOUND_CAP_EXCL_INPUT) {
319     GList *track;
320
321     for (track = mixer->tracklist; track != NULL; track = track->next) {
322       GstMixerTrack *turn = (GstMixerTrack *) track->data;
323
324       turn->flags &= ~GST_MIXER_TRACK_RECORD;
325     }
326     mixer->recdevs = 0;
327   }
328
329   /* set new record bit, if needed */
330   if (record) {
331     mixer->recdevs |= (1 << osstrack->track_num);
332   } else {
333     mixer->recdevs &= ~(1 << osstrack->track_num);
334   }
335
336   /* set it to the device */
337   if (ioctl (mixer->mixer_fd, SOUND_MIXER_WRITE_RECSRC, &mixer->recdevs) < 0) {
338     g_warning ("Error setting mixer recording devices (0x%x): %s",
339         mixer->recdevs, strerror (errno));
340     return;
341   }
342
343   if (record) {
344     track->flags |= GST_MIXER_TRACK_RECORD;
345   } else {
346     track->flags &= ~GST_MIXER_TRACK_RECORD;
347   }
348 }