gstreamer/sys/oss/gstossmixer.c
Ronald S. Bultje 4506e60ea0 Make GstMixerTrack a GObject. I also want to make it emit several signals, starting work is in here but it's not full...
Original commit message from CVS:
Make GstMixerTrack a GObject. I also want to make it emit several signals, starting work is in here but it's not fully implemented yet. for OSS, this will cause issues, but for ALSA, this is all automated.
2003-10-10 12:24:20 +00:00

411 lines
11 KiB
C

/* GStreamer OSS Mixer implementation
* Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include "gstossmixer.h"
#define MASK_BIT_IS_SET(mask, bit) \
(mask & (1 << bit))
static void gst_ossmixer_track_class_init (GstOssMixerTrackClass *klass);
static void gst_ossmixer_track_init (GstOssMixerTrack *track);
static gboolean gst_ossmixer_supported (GstInterface *iface,
GType iface_type);
static const GList *
gst_ossmixer_list_tracks (GstMixer *ossmixer);
static void gst_ossmixer_set_volume (GstMixer *ossmixer,
GstMixerTrack *track,
gint *volumes);
static void gst_ossmixer_get_volume (GstMixer *ossmixer,
GstMixerTrack *track,
gint *volumes);
static void gst_ossmixer_set_record (GstMixer *ossmixer,
GstMixerTrack *track,
gboolean record);
static void gst_ossmixer_set_mute (GstMixer *ossmixer,
GstMixerTrack *track,
gboolean mute);
static const gchar *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
static GstMixerTrackClass *parent_class = NULL;
GType
gst_ossmixer_track_get_type (void)
{
static GType gst_ossmixer_track_type = 0;
if (!gst_ossmixer_track_type) {
static const GTypeInfo ossmixer_track_info = {
sizeof (GstOssMixerTrackClass),
NULL,
NULL,
(GClassInitFunc) gst_ossmixer_track_class_init,
NULL,
NULL,
sizeof (GstOssMixerTrack),
0,
(GInstanceInitFunc) gst_ossmixer_track_init,
NULL
};
gst_ossmixer_track_type =
g_type_register_static (GST_TYPE_MIXER_TRACK,
"GstOssMixerTrack",
&ossmixer_track_info, 0);
}
return gst_ossmixer_track_type;
}
static void
gst_ossmixer_track_class_init (GstOssMixerTrackClass *klass)
{
parent_class = g_type_class_ref (GST_TYPE_MIXER_TRACK);
}
static void
gst_ossmixer_track_init (GstOssMixerTrack *track)
{
track->lvol = track->rvol = 0;
track->track_num = 0;
}
GstMixerTrack *
gst_ossmixer_track_new (GstOssElement *oss,
gint track_num,
gint max_chans,
gint flags)
{
GstOssMixerTrack *osstrack;
GstMixerTrack *track;
gint volumes[2];
osstrack = g_object_new (GST_TYPE_OSSMIXER_TRACK, NULL);
track = GST_MIXER_TRACK (osstrack);
track->label = g_strdup (labels[track_num]);
track->num_channels = max_chans;
track->flags = flags;
track->min_volume = 0;
track->max_volume = 100;
osstrack->track_num = track_num;
/* volume */
gst_ossmixer_get_volume (GST_MIXER (oss), track, volumes);
osstrack->lvol = volumes[0];
if (max_chans == 2)
osstrack->rvol = volumes[1];
return track;
}
void
gst_oss_interface_init (GstInterfaceClass *klass)
{
/* default virtual functions */
klass->supported = gst_ossmixer_supported;
}
void
gst_ossmixer_interface_init (GstMixerClass *klass)
{
/* default virtual functions */
klass->list_tracks = gst_ossmixer_list_tracks;
klass->set_volume = gst_ossmixer_set_volume;
klass->get_volume = gst_ossmixer_get_volume;
klass->set_mute = gst_ossmixer_set_mute;
klass->set_record = gst_ossmixer_set_record;
}
static gboolean
gst_ossmixer_supported (GstInterface *iface,
GType iface_type)
{
g_assert (iface_type == GST_TYPE_MIXER);
return (GST_OSSELEMENT (iface)->mixer_fd != -1);
}
static gboolean
gst_ossmixer_contains_track (GstOssElement *oss,
GstOssMixerTrack *osstrack)
{
const GList *item;
for (item = oss->tracklist; item != NULL; item = item->next)
if (item->data == osstrack)
return TRUE;
return FALSE;
}
static const GList *
gst_ossmixer_list_tracks (GstMixer *mixer)
{
return (const GList *) GST_OSSELEMENT (mixer)->tracklist;
}
static void
gst_ossmixer_get_volume (GstMixer *mixer,
GstMixerTrack *track,
gint *volumes)
{
gint volume;
GstOssElement *oss = GST_OSSELEMENT (mixer);
GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
/* assert that we're opened and that we're using a known item */
g_return_if_fail (oss->mixer_fd != -1);
g_return_if_fail (gst_ossmixer_contains_track (oss, 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(oss->mixer_fd, MIXER_READ (osstrack->track_num), &volume) < 0) {
g_warning("Error getting recording device (%d) volume (0x%x): %s\n",
osstrack->track_num, volume, strerror(errno));
volume = 0;
}
osstrack->lvol = volumes[0] = (volume & 0xff);
if (track->num_channels == 2) {
osstrack->rvol = volumes[1] = ((volume >> 8) & 0xff);
}
}
}
static void
gst_ossmixer_set_volume (GstMixer *mixer,
GstMixerTrack *track,
gint *volumes)
{
gint volume;
GstOssElement *oss = GST_OSSELEMENT (mixer);
GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
/* assert that we're opened and that we're using a known item */
g_return_if_fail (oss->mixer_fd != -1);
g_return_if_fail (gst_ossmixer_contains_track (oss, 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(oss->mixer_fd, MIXER_WRITE (osstrack->track_num), &volume) < 0) {
g_warning("Error setting recording device (%d) volume (0x%x): %s\n",
osstrack->track_num, volume, strerror(errno));
return;
}
}
osstrack->lvol = volumes[0];
if (track->num_channels == 2) {
osstrack->rvol = volumes[1];
}
}
static void
gst_ossmixer_set_mute (GstMixer *mixer,
GstMixerTrack *track,
gboolean mute)
{
int volume;
GstOssElement *oss = GST_OSSELEMENT (mixer);
GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
/* assert that we're opened and that we're using a known item */
g_return_if_fail (oss->mixer_fd != -1);
g_return_if_fail (gst_ossmixer_contains_track (oss, osstrack));
if (mute) {
volume = 0;
} else {
volume = (osstrack->lvol & 0xff);
if (MASK_BIT_IS_SET (oss->stereomask, osstrack->track_num)) {
volume |= ((osstrack->rvol & 0xff) << 8);
}
}
if (ioctl(oss->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;
}
}
static void
gst_ossmixer_set_record (GstMixer *mixer,
GstMixerTrack *track,
gboolean record)
{
GstOssElement *oss = GST_OSSELEMENT (mixer);
GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
/* assert that we're opened and that we're using a known item */
g_return_if_fail (oss->mixer_fd != -1);
g_return_if_fail (gst_ossmixer_contains_track (oss, osstrack));
/* if we're exclusive, then we need to unset the current one(s) */
if (oss->mixcaps & SOUND_CAP_EXCL_INPUT) {
GList *track;
for (track = oss->tracklist; track != NULL; track = track->next) {
GstMixerTrack *turn = (GstMixerTrack *) track->data;
turn->flags &= ~GST_MIXER_TRACK_RECORD;
}
oss->recdevs = 0;
}
/* set new record bit, if needed */
if (record) {
oss->recdevs |= (1 << osstrack->track_num);
} else {
oss->recdevs &= ~(1 << osstrack->track_num);
}
/* set it to the device */
if (ioctl(oss->mixer_fd, SOUND_MIXER_WRITE_RECSRC, &oss->recdevs) < 0) {
g_warning("Error setting mixer recording devices (0x%x): %s",
oss->recdevs, strerror(errno));
return;
}
if (record) {
track->flags |= GST_MIXER_TRACK_RECORD;
} else {
track->flags &= ~GST_MIXER_TRACK_RECORD;
}
}
void
gst_ossmixer_build_list (GstOssElement *oss)
{
gint i, devmask;
const GList *pads = gst_element_get_pad_list (GST_ELEMENT (oss));
GstPadDirection dir = GST_PAD_UNKNOWN;
struct mixer_info minfo;
g_return_if_fail (oss->mixer_fd == -1);
oss->mixer_fd = open (oss->mixer_dev, O_RDWR);
if (oss->mixer_fd == -1) {
/* this is valid. OSS devices don't need to expose a mixer */
GST_DEBUG ("Failed to open mixer device %s, mixing disabled: %s",
oss->mixer_dev, strerror (errno));
return;
}
/* get direction */
if (pads && g_list_length ((GList *) pads) == 1)
dir = GST_PAD_DIRECTION (GST_PAD (pads->data));
/* get masks */
if (ioctl (oss->mixer_fd, SOUND_MIXER_READ_RECMASK, &oss->recmask) < 0 ||
ioctl (oss->mixer_fd, SOUND_MIXER_READ_RECSRC, &oss->recdevs) < 0 ||
ioctl (oss->mixer_fd, SOUND_MIXER_READ_STEREODEVS, &oss->stereomask) < 0 ||
ioctl (oss->mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0 ||
ioctl (oss->mixer_fd, SOUND_MIXER_READ_CAPS, &oss->mixcaps) < 0) {
GST_DEBUG ("Failed to get device masks - disabling mixer");
close (oss->mixer_fd);
oss->mixer_fd = -1;
return;
}
/* get name */
if (ioctl (oss->mixer_fd, SOUND_MIXER_INFO, &minfo) == 0) {
oss->device_name = g_strdup (minfo.name);
}
/* build track list */
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
if (devmask & (1 << i)) {
GstMixerTrack *track;
gboolean input = FALSE, stereo = FALSE, record = FALSE;
/* track exists, make up capabilities */
if (MASK_BIT_IS_SET (oss->stereomask, i))
stereo = TRUE;
if (MASK_BIT_IS_SET (oss->recmask, i))
input = TRUE;
if (MASK_BIT_IS_SET (oss->recdevs, i))
record = TRUE;
/* do we want this in our list? */
if ((dir == GST_PAD_SRC && input == FALSE) ||
(dir == GST_PAD_SINK && i == SOUND_MIXER_PCM))
continue;
/* add track to list */
track = gst_ossmixer_track_new (oss, i, stereo ? 2 : 1,
(record ? GST_MIXER_TRACK_RECORD : 0) |
(input ? GST_MIXER_TRACK_INPUT :
GST_MIXER_TRACK_OUTPUT));
oss->tracklist = g_list_append (oss->tracklist, track);
}
}
}
void
gst_ossmixer_free_list (GstOssElement *oss)
{
if (oss->mixer_fd == -1)
return;
g_list_foreach (oss->tracklist, (GFunc) g_object_unref, NULL);
g_list_free (oss->tracklist);
oss->tracklist = NULL;
if (oss->device_name) {
g_free (oss->device_name);
oss->device_name = NULL;
}
close (oss->mixer_fd);
oss->mixer_fd = -1;
}