1 /* GStreamer OSS Mixer implementation
2 * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
4 * gstossmixer.c: mixer interface implementation for OSS
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.
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.
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.
32 #include <sys/ioctl.h>
33 #include <sys/soundcard.h>
35 #include <gst/gst-i18n-plugin.h>
37 #include "gstossmixer.h"
38 #include "gstossmixertrack.h"
40 #define MASK_BIT_IS_SET(mask, bit) \
44 gst_ossmixer_open (GstOssMixer * mixer)
46 #ifdef SOUND_MIXER_INFO
47 struct mixer_info minfo;
50 g_return_val_if_fail (mixer->mixer_fd == -1, FALSE);
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));
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);
74 #ifdef SOUND_MIXER_INFO
75 if (ioctl (mixer->mixer_fd, SOUND_MIXER_INFO, &minfo) == 0) {
76 mixer->cardname = g_strdup (minfo.name);
79 oss->cardname = g_strdup ("Unknown");
86 gst_ossmixer_ensure_track_list (GstOssMixer * mixer)
90 g_return_if_fail (mixer->mixer_fd != -1);
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 */
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;
110 /* track exists, make up capabilities */
111 if (MASK_BIT_IS_SET (mixer->stereomask, i))
113 if (MASK_BIT_IS_SET (mixer->recmask, i))
115 if (MASK_BIT_IS_SET (mixer->recdevs, i))
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 */
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);
136 gst_ossmixer_new (const char *device, GstOssMixerDirection dir)
138 GstOssMixer *ret = NULL;
140 g_return_val_if_fail (device != NULL, NULL);
142 ret = g_new0 (GstOssMixer, 1);
144 ret->device = g_strdup (device);
148 if (!gst_ossmixer_open (ret))
155 gst_ossmixer_free (ret);
161 gst_ossmixer_free (GstOssMixer * mixer)
163 g_return_if_fail (mixer != NULL);
166 g_free (mixer->device);
167 mixer->device = NULL;
170 if (mixer->cardname) {
171 g_free (mixer->cardname);
172 mixer->cardname = NULL;
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;
181 if (mixer->mixer_fd != -1) {
182 close (mixer->mixer_fd);
183 mixer->mixer_fd = -1;
189 /* unused with G_DISABLE_* */
190 static G_GNUC_UNUSED gboolean
191 gst_ossmixer_contains_track (GstOssMixer * mixer, GstOssMixerTrack * osstrack)
195 for (item = mixer->tracklist; item != NULL; item = item->next)
196 if (item->data == osstrack)
203 gst_ossmixer_list_tracks (GstOssMixer * mixer)
205 gst_ossmixer_ensure_track_list (mixer);
207 return (const GList *) mixer->tracklist;
211 gst_ossmixer_get_volume (GstOssMixer * mixer,
212 GstMixerTrack * track, gint * volumes)
215 GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
217 g_return_if_fail (mixer->mixer_fd != -1);
218 g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
220 if (track->flags & GST_MIXER_TRACK_MUTE) {
221 volumes[0] = osstrack->lvol;
222 if (track->num_channels == 2) {
223 volumes[1] = osstrack->rvol;
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));
233 osstrack->lvol = volumes[0] = (volume & 0xff);
234 if (track->num_channels == 2) {
235 osstrack->rvol = volumes[1] = ((volume >> 8) & 0xff);
241 gst_ossmixer_set_volume (GstOssMixer * mixer,
242 GstMixerTrack * track, gint * volumes)
245 GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
247 g_return_if_fail (mixer->mixer_fd != -1);
248 g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
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);
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));
265 osstrack->lvol = volumes[0];
266 if (track->num_channels == 2) {
267 osstrack->rvol = volumes[1];
272 gst_ossmixer_set_mute (GstOssMixer * mixer, GstMixerTrack * track,
276 GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
278 g_return_if_fail (mixer->mixer_fd != -1);
279 g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
284 volume = (osstrack->lvol & 0xff);
285 if (MASK_BIT_IS_SET (mixer->stereomask, osstrack->track_num)) {
286 volume |= ((osstrack->rvol & 0xff) << 8);
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));
297 track->flags |= GST_MIXER_TRACK_MUTE;
299 track->flags &= ~GST_MIXER_TRACK_MUTE;
304 gst_ossmixer_set_record (GstOssMixer * mixer,
305 GstMixerTrack * track, gboolean record)
307 GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
309 g_return_if_fail (mixer->mixer_fd != -1);
310 g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
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)))
317 /* if we're exclusive, then we need to unset the current one(s) */
318 if (mixer->mixcaps & SOUND_CAP_EXCL_INPUT) {
321 for (track = mixer->tracklist; track != NULL; track = track->next) {
322 GstMixerTrack *turn = (GstMixerTrack *) track->data;
324 turn->flags &= ~GST_MIXER_TRACK_RECORD;
329 /* set new record bit, if needed */
331 mixer->recdevs |= (1 << osstrack->track_num);
333 mixer->recdevs &= ~(1 << osstrack->track_num);
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));
344 track->flags |= GST_MIXER_TRACK_RECORD;
346 track->flags &= ~GST_MIXER_TRACK_RECORD;