diff options
author | Michal Ratajsky <[email protected]> | 2014-08-12 05:06:12 +0200 |
---|---|---|
committer | Michal Ratajsky <[email protected]> | 2014-08-12 05:06:12 +0200 |
commit | 1e1847069eb58c2b62f2b3c11e1e9adf3a17ebde (patch) | |
tree | 4bcd30db2bdf4032c4a49844570ce4227cf2510e /backends/oss/oss-device.c | |
parent | 7396148c328d9f2a0d933374547c7e93a46b8efa (diff) | |
parent | 6c6d4239ddc807e922df3874654f99eea291aadb (diff) | |
download | libmatemixer-1e1847069eb58c2b62f2b3c11e1e9adf3a17ebde.tar.bz2 libmatemixer-1e1847069eb58c2b62f2b3c11e1e9adf3a17ebde.tar.xz |
Merge branch 'oss-alsa'
Diffstat (limited to 'backends/oss/oss-device.c')
-rw-r--r-- | backends/oss/oss-device.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/backends/oss/oss-device.c b/backends/oss/oss-device.c new file mode 100644 index 0000000..cf51705 --- /dev/null +++ b/backends/oss/oss-device.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <unistd.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "oss-common.h" +#include "oss-device.h" +#include "oss-stream.h" +#include "oss-stream-control.h" + +#define OSS_DEVICE_ICON "audio-card" + +typedef enum +{ + OSS_DEV_ANY, + OSS_DEV_INPUT, + OSS_DEV_OUTPUT +} OssDevType; + +typedef struct +{ + gchar *name; + gchar *label; + MateMixerStreamControlRole role; + OssDevType type; +} OssDev; + +static const OssDev oss_devices[] = { + { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_OUTPUT }, + { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, OSS_DEV_OUTPUT }, + { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, OSS_DEV_OUTPUT }, + { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }, + { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT }, + /* OSS manual says this should be the beeper, but Linux OSS seems to assign it to + * regular volume control */ + { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, OSS_DEV_OUTPUT }, + { "line", N_("Line-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, OSS_DEV_INPUT }, + /* Recording monitor */ + { "mix", N_("Mixer"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "pcm2", N_("PCM-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT }, + /* Recording level (master input) */ + { "rec", N_("Record"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_INPUT }, + { "igain", N_("In-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }, + { "ogain", N_("Out-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "line1", N_("Line-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "line2", N_("Line-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "line3", N_("Line-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "dig1", N_("Digital-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY }, + { "dig2", N_("Digital-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY }, + { "dig3", N_("Digital-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY }, + { "phin", N_("Phone-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "phout", N_("Phone-out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_OUTPUT }, + { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "monitor", N_("Monitor"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "depth", N_("3D-depth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "center", N_("3D-center"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "midi", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT } +}; + +#define OSS_N_DEVICES MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES) + +struct _OssDevicePrivate +{ + gint fd; + gchar *path; + gint devmask; + gint stereodevs; + gint recmask; + gint recsrc; + OssStream *input; + OssStream *output; +}; + +static void oss_device_class_init (OssDeviceClass *klass); +static void oss_device_init (OssDevice *device); +static void oss_device_dispose (GObject *object); +static void oss_device_finalize (GObject *object); + +G_DEFINE_TYPE (OssDevice, oss_device, MATE_MIXER_TYPE_DEVICE) + +static GList * oss_device_list_streams (MateMixerDevice *device); + +static gboolean read_mixer_devices (OssDevice *device); + +static gboolean set_stream_default_control (OssStream *stream, + OssStreamControl *control, + gboolean force); + +static void +oss_device_class_init (OssDeviceClass *klass) +{ + GObjectClass *object_class; + MateMixerDeviceClass *device_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = oss_device_dispose; + object_class->finalize = oss_device_finalize; + + device_class = MATE_MIXER_DEVICE_CLASS (klass); + device_class->list_streams = oss_device_list_streams; + + g_type_class_add_private (object_class, sizeof (OssDevicePrivate)); +} + +static void +oss_device_init (OssDevice *device) +{ + device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, + OSS_TYPE_DEVICE, + OssDevicePrivate); +} + +static void +oss_device_dispose (GObject *object) +{ + OssDevice *device; + + device = OSS_DEVICE (object); + + g_clear_object (&device->priv->input); + g_clear_object (&device->priv->output); + + G_OBJECT_CLASS (oss_device_parent_class)->dispose (object); +} + +static void +oss_device_finalize (GObject *object) +{ + OssDevice *device = OSS_DEVICE (object); + + close (device->priv->fd); + + G_OBJECT_CLASS (oss_device_parent_class)->finalize (object); +} + +OssDevice * +oss_device_new (const gchar *name, const gchar *label, const gchar *path, gint fd) +{ + OssDevice *device; + gchar *stream_name; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (OSS_TYPE_DEVICE, + "name", name, + "label", label, + "icon", OSS_DEVICE_ICON, + NULL); + + device->priv->fd = dup (fd); + device->priv->path = g_strdup (path); + + stream_name = g_strdup_printf ("oss-input-%s", name); + device->priv->input = oss_stream_new (stream_name, + MATE_MIXER_DEVICE (device), + MATE_MIXER_STREAM_INPUT); + g_free (stream_name); + + stream_name = g_strdup_printf ("oss-output-%s", name); + device->priv->output = oss_stream_new (stream_name, + MATE_MIXER_DEVICE (device), + MATE_MIXER_STREAM_OUTPUT); + g_free (stream_name); + + return device; +} + +gboolean +oss_device_open (OssDevice *device) +{ + gint ret; + + g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); + + g_debug ("Opening device %s (%s)", + device->priv->path, + mate_mixer_device_get_label (MATE_MIXER_DEVICE (device))); + + /* Read the essential information about the device, these values are not + * expected to change and will not be queried */ + ret = ioctl (device->priv->fd, + MIXER_READ (SOUND_MIXER_DEVMASK), + &device->priv->devmask); + if (ret != 0) + goto fail; + + ret = ioctl (device->priv->fd, + MIXER_READ (SOUND_MIXER_STEREODEVS), + &device->priv->stereodevs); + if (ret < 0) + goto fail; + + ret = ioctl (device->priv->fd, + MIXER_READ (SOUND_MIXER_RECMASK), + &device->priv->recmask); + if (ret < 0) + goto fail; + + /* The recording source mask may change at any time, here we just read + * the initial value */ + ret = ioctl (device->priv->fd, + MIXER_READ (SOUND_MIXER_RECSRC), + &device->priv->recsrc); + if (ret < 0) + goto fail; + + /* NOTE: Linux also supports SOUND_MIXER_OUTSRC and SOUND_MIXER_OUTMASK which + * inform about/enable input->output, we could potentially create toggles + * for these, but these constants are not defined on any BSD. */ + + return TRUE; + +fail: + g_warning ("Failed to read device %s: %s", + device->priv->path, + g_strerror (errno)); + + return FALSE; +} + +gboolean +oss_device_load (OssDevice *device) +{ + MateMixerStreamControl *control; + + g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); + + read_mixer_devices (device); + + control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->input)); + if (control == NULL) { + // XXX pick something + } + + if (control != NULL) + g_debug ("Default input stream control is %s", + mate_mixer_stream_control_get_label (control)); + + control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->output)); + if (control == NULL) { + // XXX pick something + } + + if (control != NULL) + g_debug ("Default output stream control is %s", + mate_mixer_stream_control_get_label (control)); + + return TRUE; +} + +gint +oss_device_get_fd (OssDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), -1); + + return device->priv->fd; +} + +const gchar * +oss_device_get_path (OssDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + + return device->priv->path; +} + +OssStream * +oss_device_get_input_stream (OssDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + + return device->priv->input; +} + +OssStream * +oss_device_get_output_stream (OssDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + + return device->priv->output; +} + +static GList * +oss_device_list_streams (MateMixerDevice *mmd) +{ + OssDevice *device; + GList *list = NULL; + + g_return_val_if_fail (OSS_IS_DEVICE (mmd), NULL); + + device = OSS_DEVICE (mmd); + + if (device->priv->output != NULL) + list = g_list_prepend (list, g_object_ref (device->priv->output)); + if (device->priv->input != NULL) + list = g_list_prepend (list, g_object_ref (device->priv->input)); + + return list; +} + +#define OSS_MASK_HAS_DEVICE(mask,i) ((gboolean) (((mask) & (1 << (i))) > 0)) + +static gboolean +read_mixer_devices (OssDevice *device) +{ + gint i; + + for (i = 0; i < OSS_N_DEVICES; i++) { + OssStreamControl *control; + gboolean input = FALSE; + + /* Skip unavailable controls */ + if (OSS_MASK_HAS_DEVICE (device->priv->devmask, i) == FALSE) + continue; + + if (oss_devices[i].type == OSS_DEV_ANY) { + input = OSS_MASK_HAS_DEVICE (device->priv->recmask, i); + } + else if (oss_devices[i].type == OSS_DEV_INPUT) { + input = TRUE; + } + + control = oss_stream_control_new (oss_devices[i].name, + oss_devices[i].label, + oss_devices[i].role, + device->priv->fd, + i, + OSS_MASK_HAS_DEVICE (device->priv->stereodevs, i)); + + if (input == TRUE) { + oss_stream_add_control (OSS_STREAM (device->priv->input), control); + + if (i == SOUND_MIXER_RECLEV || i == SOUND_MIXER_IGAIN) { + if (i == SOUND_MIXER_RECLEV) + set_stream_default_control (OSS_STREAM (device->priv->input), + control, + TRUE); + else + set_stream_default_control (OSS_STREAM (device->priv->input), + control, + FALSE); + } + } else { + oss_stream_add_control (OSS_STREAM (device->priv->output), control); + + if (i == SOUND_MIXER_VOLUME || i == SOUND_MIXER_PCM) { + if (i == SOUND_MIXER_VOLUME) + set_stream_default_control (OSS_STREAM (device->priv->output), + control, + TRUE); + else + set_stream_default_control (OSS_STREAM (device->priv->output), + control, + FALSE); + } + } + + g_debug ("Added control %s", + mate_mixer_stream_control_get_label (MATE_MIXER_STREAM_CONTROL (control))); + + oss_stream_control_update (control); + } + return TRUE; +} + +static gboolean +set_stream_default_control (OssStream *stream, OssStreamControl *control, gboolean force) +{ + MateMixerStreamControl *current; + + current = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)); + if (current == NULL || force == TRUE) { + oss_stream_set_default_control (stream, OSS_STREAM_CONTROL (control)); + return TRUE; + } + return FALSE; +} |