/* GStreamer OSS Mixer implementation * Copyright (C) 2003 Ronald Bultje * * gstossmixer.c: mixer interface implementation for OSS * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifdef HAVE_OSS_INCLUDE_IN_SYS # include #else # ifdef HAVE_OSS_INCLUDE_IN_ROOT # include # else # ifdef HAVE_OSS_INCLUDE_IN_MACHINE # include # else # error "What to include?" # endif /* HAVE_OSS_INCLUDE_IN_MACHINE */ # endif /* HAVE_OSS_INCLUDE_IN_ROOT */ #endif /* HAVE_OSS_INCLUDE_IN_SYS */ #include #include "gstossmixer.h" #include "gstossmixertrack.h" GST_DEBUG_CATEGORY_EXTERN (oss_debug); #define GST_CAT_DEFAULT oss_debug #define MASK_BIT_IS_SET(mask, bit) \ (mask & (1 << bit)) static gboolean gst_ossmixer_open (GstOssMixer * mixer) { #ifdef SOUND_MIXER_INFO struct mixer_info minfo; #endif g_return_val_if_fail (mixer->mixer_fd == -1, FALSE); mixer->mixer_fd = open (mixer->device, O_RDWR); if (mixer->mixer_fd == -1) goto open_failed; /* get masks */ if (ioctl (mixer->mixer_fd, SOUND_MIXER_READ_RECMASK, &mixer->recmask) < 0 || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_RECSRC, &mixer->recdevs) < 0 || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_STEREODEVS, &mixer->stereomask) < 0 || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_DEVMASK, &mixer->devmask) < 0 || ioctl (mixer->mixer_fd, SOUND_MIXER_READ_CAPS, &mixer->mixcaps) < 0) goto masks_failed; /* get name, not fatal */ g_free (mixer->cardname); #ifdef SOUND_MIXER_INFO if (ioctl (mixer->mixer_fd, SOUND_MIXER_INFO, &minfo) == 0) { mixer->cardname = g_strdup (minfo.name); GST_INFO ("Card name = %s", GST_STR_NULL (mixer->cardname)); } else #endif { mixer->cardname = g_strdup ("Unknown"); GST_INFO ("Unknown card name"); } GST_INFO ("Opened mixer for device %s", mixer->device); return TRUE; /* ERRORS */ open_failed: { /* this is valid. OSS devices don't need to expose a mixer */ GST_DEBUG ("Failed to open mixer device %s, mixing disabled: %s", mixer->device, strerror (errno)); return FALSE; } masks_failed: { GST_DEBUG ("Failed to get device masks"); close (mixer->mixer_fd); mixer->mixer_fd = -1; return FALSE; } } static void gst_ossmixer_ensure_track_list (GstOssMixer * mixer) { gint i, master = -1; g_return_if_fail (mixer->mixer_fd != -1); if (mixer->tracklist) return; /* find master volume */ if (mixer->devmask & SOUND_MASK_VOLUME) master = SOUND_MIXER_VOLUME; else if (mixer->devmask & SOUND_MASK_PCM) master = SOUND_MIXER_PCM; else if (mixer->devmask & SOUND_MASK_SPEAKER) master = SOUND_MIXER_SPEAKER; /* doubtful... */ /* else: no master, so we won't set any */ /* build track list */ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (mixer->devmask & (1 << i)) { GstMixerTrack *track; gboolean input = FALSE, stereo = FALSE, record = FALSE; /* track exists, make up capabilities */ if (MASK_BIT_IS_SET (mixer->stereomask, i)) stereo = TRUE; if (MASK_BIT_IS_SET (mixer->recmask, i)) input = TRUE; if (MASK_BIT_IS_SET (mixer->recdevs, i)) record = TRUE; /* do we want this in our list? */ if (!((mixer->dir & GST_OSS_MIXER_CAPTURE && input == TRUE) || (mixer->dir & GST_OSS_MIXER_PLAYBACK && i != SOUND_MIXER_PCM))) /* the PLAYBACK case seems hacky, but that's how 0.8 had it */ continue; /* add track to list */ track = gst_ossmixer_track_new (mixer->mixer_fd, i, stereo ? 2 : 1, (record ? GST_MIXER_TRACK_RECORD : 0) | (input ? GST_MIXER_TRACK_INPUT : GST_MIXER_TRACK_OUTPUT) | ((master != i) ? 0 : GST_MIXER_TRACK_MASTER)); mixer->tracklist = g_list_append (mixer->tracklist, track); } } } GstOssMixer * gst_ossmixer_new (const char *device, GstOssMixerDirection dir) { GstOssMixer *ret = NULL; g_return_val_if_fail (device != NULL, NULL); ret = g_new0 (GstOssMixer, 1); ret->device = g_strdup (device); ret->dir = dir; ret->mixer_fd = -1; if (!gst_ossmixer_open (ret)) goto error; return ret; /* ERRORS */ error: { gst_ossmixer_free (ret); return NULL; } } void gst_ossmixer_free (GstOssMixer * mixer) { g_return_if_fail (mixer != NULL); if (mixer->device) { g_free (mixer->device); mixer->device = NULL; } if (mixer->cardname) { g_free (mixer->cardname); mixer->cardname = NULL; } if (mixer->tracklist) { g_list_foreach (mixer->tracklist, (GFunc) g_object_unref, NULL); g_list_free (mixer->tracklist); mixer->tracklist = NULL; } if (mixer->mixer_fd != -1) { close (mixer->mixer_fd); mixer->mixer_fd = -1; } g_free (mixer); } /* unused with G_DISABLE_* */ static G_GNUC_UNUSED gboolean gst_ossmixer_contains_track (GstOssMixer * mixer, GstOssMixerTrack * osstrack) { const GList *item; for (item = mixer->tracklist; item != NULL; item = item->next) if (item->data == osstrack) return TRUE; return FALSE; } const GList * gst_ossmixer_list_tracks (GstOssMixer * mixer) { gst_ossmixer_ensure_track_list (mixer); return (const GList *) mixer->tracklist; } void gst_ossmixer_get_volume (GstOssMixer * mixer, GstMixerTrack * track, gint * volumes) { gint volume; GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track); g_return_if_fail (mixer->mixer_fd != -1); g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack)); if (track->flags & GST_MIXER_TRACK_MUTE) { volumes[0] = osstrack->lvol; if (track->num_channels == 2) { volumes[1] = osstrack->rvol; } } else { /* get */ if (ioctl (mixer->mixer_fd, MIXER_READ (osstrack->track_num), &volume) < 0) { g_warning ("Error getting recording device (%d) volume: %s", osstrack->track_num, strerror (errno)); volume = 0; } osstrack->lvol = volumes[0] = (volume & 0xff); if (track->num_channels == 2) { osstrack->rvol = volumes[1] = ((volume >> 8) & 0xff); } } } void gst_ossmixer_set_volume (GstOssMixer * mixer, GstMixerTrack * track, gint * volumes) { gint volume; GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track); g_return_if_fail (mixer->mixer_fd != -1); g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack)); /* prepare the value for ioctl() */ if (!(track->flags & GST_MIXER_TRACK_MUTE)) { volume = (volumes[0] & 0xff); if (track->num_channels == 2) { volume |= ((volumes[1] & 0xff) << 8); } /* set */ if (ioctl (mixer->mixer_fd, MIXER_WRITE (osstrack->track_num), &volume) < 0) { g_warning ("Error setting recording device (%d) volume (0x%x): %s", osstrack->track_num, volume, strerror (errno)); return; } } osstrack->lvol = volumes[0]; if (track->num_channels == 2) { osstrack->rvol = volumes[1]; } } void gst_ossmixer_set_mute (GstOssMixer * mixer, GstMixerTrack * track, gboolean mute) { int volume; GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track); g_return_if_fail (mixer->mixer_fd != -1); g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack)); if (mute) { volume = 0; } else { volume = (osstrack->lvol & 0xff); if (MASK_BIT_IS_SET (mixer->stereomask, osstrack->track_num)) { volume |= ((osstrack->rvol & 0xff) << 8); } } if (ioctl (mixer->mixer_fd, MIXER_WRITE (osstrack->track_num), &volume) < 0) { g_warning ("Error setting mixer recording device volume (0x%x): %s", volume, strerror (errno)); return; } if (mute) { track->flags |= GST_MIXER_TRACK_MUTE; } else { track->flags &= ~GST_MIXER_TRACK_MUTE; } } void gst_ossmixer_set_record (GstOssMixer * mixer, GstMixerTrack * track, gboolean record) { GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track); g_return_if_fail (mixer->mixer_fd != -1); g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack)); /* if there's nothing to do... */ if ((record && GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)) || (!record && !GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD))) return; /* if we're exclusive, then we need to unset the current one(s) */ if (mixer->mixcaps & SOUND_CAP_EXCL_INPUT) { GList *track; for (track = mixer->tracklist; track != NULL; track = track->next) { GstMixerTrack *turn = (GstMixerTrack *) track->data; turn->flags &= ~GST_MIXER_TRACK_RECORD; } mixer->recdevs = 0; } /* set new record bit, if needed */ if (record) { mixer->recdevs |= (1 << osstrack->track_num); } else { mixer->recdevs &= ~(1 << osstrack->track_num); } /* set it to the device */ if (ioctl (mixer->mixer_fd, SOUND_MIXER_WRITE_RECSRC, &mixer->recdevs) < 0) { g_warning ("Error setting mixer recording devices (0x%x): %s", mixer->recdevs, strerror (errno)); return; } if (record) { track->flags |= GST_MIXER_TRACK_RECORD; } else { track->flags &= ~GST_MIXER_TRACK_RECORD; } }