Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.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
34 #ifdef HAVE_OSS_INCLUDE_IN_SYS
35 # include <sys/soundcard.h>
36 #else
37 # ifdef HAVE_OSS_INCLUDE_IN_ROOT
38 #  include <soundcard.h>
39 # else
40 #  ifdef HAVE_OSS_INCLUDE_IN_MACHINE
41 #   include <machine/soundcard.h>
42 #  else
43 #   error "What to include?"
44 #  endif /* HAVE_OSS_INCLUDE_IN_MACHINE */
45 # endif /* HAVE_OSS_INCLUDE_IN_ROOT */
46 #endif /* HAVE_OSS_INCLUDE_IN_SYS */
47
48 #include <gst/gst-i18n-plugin.h>
49
50 #include "gstossmixer.h"
51 #include "gstossmixertrack.h"
52
53 GST_DEBUG_CATEGORY_EXTERN (oss_debug);
54 #define GST_CAT_DEFAULT oss_debug
55
56 #define MASK_BIT_IS_SET(mask, bit) \
57   (mask & (1 << bit))
58
59 static gboolean
60 gst_ossmixer_open (GstOssMixer * mixer)
61 {
62 #ifdef SOUND_MIXER_INFO
63   struct mixer_info minfo;
64 #endif
65
66   g_return_val_if_fail (mixer->mixer_fd == -1, FALSE);
67
68   mixer->mixer_fd = open (mixer->device, O_RDWR);
69   if (mixer->mixer_fd == -1)
70     goto open_failed;
71
72   /* get masks */
73   if (ioctl (mixer->mixer_fd, SOUND_MIXER_READ_RECMASK, &mixer->recmask) < 0
74       || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_RECSRC, &mixer->recdevs) < 0
75       || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_STEREODEVS,
76           &mixer->stereomask) < 0
77       || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_DEVMASK, &mixer->devmask) < 0
78       || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_CAPS, &mixer->mixcaps) < 0)
79     goto masks_failed;
80
81   /* get name, not fatal */
82   g_free (mixer->cardname);
83 #ifdef SOUND_MIXER_INFO
84   if (ioctl (mixer->mixer_fd, SOUND_MIXER_INFO, &minfo) == 0) {
85     mixer->cardname = g_strdup (minfo.name);
86     GST_INFO ("Card name = %s", GST_STR_NULL (mixer->cardname));
87   } else
88 #endif
89   {
90     mixer->cardname = g_strdup ("Unknown");
91     GST_INFO ("Unknown card name");
92   }
93   GST_INFO ("Opened mixer for device %s", mixer->device);
94
95   return TRUE;
96
97   /* ERRORS */
98 open_failed:
99   {
100     /* this is valid. OSS devices don't need to expose a mixer */
101     GST_DEBUG ("Failed to open mixer device %s, mixing disabled: %s",
102         mixer->device, strerror (errno));
103     return FALSE;
104   }
105 masks_failed:
106   {
107     GST_DEBUG ("Failed to get device masks");
108     close (mixer->mixer_fd);
109     mixer->mixer_fd = -1;
110     return FALSE;
111   }
112 }
113
114 static void
115 gst_ossmixer_ensure_track_list (GstOssMixer * mixer)
116 {
117   gint i, master = -1;
118
119   g_return_if_fail (mixer->mixer_fd != -1);
120
121   if (mixer->tracklist)
122     return;
123
124   /* find master volume */
125   if (mixer->devmask & SOUND_MASK_VOLUME)
126     master = SOUND_MIXER_VOLUME;
127   else if (mixer->devmask & SOUND_MASK_PCM)
128     master = SOUND_MIXER_PCM;
129   else if (mixer->devmask & SOUND_MASK_SPEAKER)
130     master = SOUND_MIXER_SPEAKER;       /* doubtful... */
131   /* else: no master, so we won't set any */
132
133   /* build track list */
134   for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
135     if (mixer->devmask & (1 << i)) {
136       GstMixerTrack *track;
137       gboolean input = FALSE, stereo = FALSE, record = FALSE;
138
139       /* track exists, make up capabilities */
140       if (MASK_BIT_IS_SET (mixer->stereomask, i))
141         stereo = TRUE;
142       if (MASK_BIT_IS_SET (mixer->recmask, i))
143         input = TRUE;
144       if (MASK_BIT_IS_SET (mixer->recdevs, i))
145         record = TRUE;
146
147       /* do we want this in our list? */
148       if (!((mixer->dir & GST_OSS_MIXER_CAPTURE && input == TRUE) ||
149               (mixer->dir & GST_OSS_MIXER_PLAYBACK && i != SOUND_MIXER_PCM)))
150         /* the PLAYBACK case seems hacky, but that's how 0.8 had it */
151         continue;
152
153       /* add track to list */
154       track = gst_ossmixer_track_new (mixer->mixer_fd, i, stereo ? 2 : 1,
155           (record ? GST_MIXER_TRACK_RECORD : 0) |
156           (input ? GST_MIXER_TRACK_INPUT :
157               GST_MIXER_TRACK_OUTPUT) |
158           ((master != i) ? 0 : GST_MIXER_TRACK_MASTER));
159       mixer->tracklist = g_list_append (mixer->tracklist, track);
160     }
161   }
162 }
163
164 GstOssMixer *
165 gst_ossmixer_new (const char *device, GstOssMixerDirection dir)
166 {
167   GstOssMixer *ret = NULL;
168
169   g_return_val_if_fail (device != NULL, NULL);
170
171   ret = g_new0 (GstOssMixer, 1);
172
173   ret->device = g_strdup (device);
174   ret->dir = dir;
175   ret->mixer_fd = -1;
176
177   if (!gst_ossmixer_open (ret))
178     goto error;
179
180   return ret;
181
182   /* ERRORS */
183 error:
184   {
185     gst_ossmixer_free (ret);
186     return NULL;
187   }
188 }
189
190 void
191 gst_ossmixer_free (GstOssMixer * mixer)
192 {
193   g_return_if_fail (mixer != NULL);
194
195   if (mixer->device) {
196     g_free (mixer->device);
197     mixer->device = NULL;
198   }
199
200   if (mixer->cardname) {
201     g_free (mixer->cardname);
202     mixer->cardname = NULL;
203   }
204
205   if (mixer->tracklist) {
206     g_list_foreach (mixer->tracklist, (GFunc) g_object_unref, NULL);
207     g_list_free (mixer->tracklist);
208     mixer->tracklist = NULL;
209   }
210
211   if (mixer->mixer_fd != -1) {
212     close (mixer->mixer_fd);
213     mixer->mixer_fd = -1;
214   }
215
216   g_free (mixer);
217 }
218
219 /* unused with G_DISABLE_* */
220 static G_GNUC_UNUSED gboolean
221 gst_ossmixer_contains_track (GstOssMixer * mixer, GstOssMixerTrack * osstrack)
222 {
223   const GList *item;
224
225   for (item = mixer->tracklist; item != NULL; item = item->next)
226     if (item->data == osstrack)
227       return TRUE;
228
229   return FALSE;
230 }
231
232 const GList *
233 gst_ossmixer_list_tracks (GstOssMixer * mixer)
234 {
235   gst_ossmixer_ensure_track_list (mixer);
236
237   return (const GList *) mixer->tracklist;
238 }
239
240 void
241 gst_ossmixer_get_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   if (track->flags & GST_MIXER_TRACK_MUTE) {
251     volumes[0] = osstrack->lvol;
252     if (track->num_channels == 2) {
253       volumes[1] = osstrack->rvol;
254     }
255   } else {
256     /* get */
257     if (ioctl (mixer->mixer_fd, MIXER_READ (osstrack->track_num), &volume) < 0) {
258       g_warning ("Error getting recording device (%d) volume: %s",
259           osstrack->track_num, strerror (errno));
260       volume = 0;
261     }
262
263     osstrack->lvol = volumes[0] = (volume & 0xff);
264     if (track->num_channels == 2) {
265       osstrack->rvol = volumes[1] = ((volume >> 8) & 0xff);
266     }
267   }
268 }
269
270 void
271 gst_ossmixer_set_volume (GstOssMixer * mixer,
272     GstMixerTrack * track, gint * volumes)
273 {
274   gint volume;
275   GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
276
277   g_return_if_fail (mixer->mixer_fd != -1);
278   g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
279
280   /* prepare the value for ioctl() */
281   if (!(track->flags & GST_MIXER_TRACK_MUTE)) {
282     volume = (volumes[0] & 0xff);
283     if (track->num_channels == 2) {
284       volume |= ((volumes[1] & 0xff) << 8);
285     }
286
287     /* set */
288     if (ioctl (mixer->mixer_fd, MIXER_WRITE (osstrack->track_num), &volume) < 0) {
289       g_warning ("Error setting recording device (%d) volume (0x%x): %s",
290           osstrack->track_num, volume, strerror (errno));
291       return;
292     }
293   }
294
295   osstrack->lvol = volumes[0];
296   if (track->num_channels == 2) {
297     osstrack->rvol = volumes[1];
298   }
299 }
300
301 void
302 gst_ossmixer_set_mute (GstOssMixer * mixer, GstMixerTrack * track,
303     gboolean mute)
304 {
305   int volume;
306   GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
307
308   g_return_if_fail (mixer->mixer_fd != -1);
309   g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
310
311   if (mute) {
312     volume = 0;
313   } else {
314     volume = (osstrack->lvol & 0xff);
315     if (MASK_BIT_IS_SET (mixer->stereomask, osstrack->track_num)) {
316       volume |= ((osstrack->rvol & 0xff) << 8);
317     }
318   }
319
320   if (ioctl (mixer->mixer_fd, MIXER_WRITE (osstrack->track_num), &volume) < 0) {
321     g_warning ("Error setting mixer recording device volume (0x%x): %s",
322         volume, strerror (errno));
323     return;
324   }
325
326   if (mute) {
327     track->flags |= GST_MIXER_TRACK_MUTE;
328   } else {
329     track->flags &= ~GST_MIXER_TRACK_MUTE;
330   }
331 }
332
333 void
334 gst_ossmixer_set_record (GstOssMixer * mixer,
335     GstMixerTrack * track, gboolean record)
336 {
337   GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
338
339   g_return_if_fail (mixer->mixer_fd != -1);
340   g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
341
342   /* if there's nothing to do... */
343   if ((record && GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)) ||
344       (!record && !GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)))
345     return;
346
347   /* if we're exclusive, then we need to unset the current one(s) */
348   if (mixer->mixcaps & SOUND_CAP_EXCL_INPUT) {
349     GList *track;
350
351     for (track = mixer->tracklist; track != NULL; track = track->next) {
352       GstMixerTrack *turn = (GstMixerTrack *) track->data;
353
354       turn->flags &= ~GST_MIXER_TRACK_RECORD;
355     }
356     mixer->recdevs = 0;
357   }
358
359   /* set new record bit, if needed */
360   if (record) {
361     mixer->recdevs |= (1 << osstrack->track_num);
362   } else {
363     mixer->recdevs &= ~(1 << osstrack->track_num);
364   }
365
366   /* set it to the device */
367   if (ioctl (mixer->mixer_fd, SOUND_MIXER_WRITE_RECSRC, &mixer->recdevs) < 0) {
368     g_warning ("Error setting mixer recording devices (0x%x): %s",
369         mixer->recdevs, strerror (errno));
370     return;
371   }
372
373   if (record) {
374     track->flags |= GST_MIXER_TRACK_RECORD;
375   } else {
376     track->flags &= ~GST_MIXER_TRACK_RECORD;
377   }
378 }