From 59a9aabf7b66e130f68b797c5a3674798fae437b Mon Sep 17 00:00:00 2001 From: Michal Ratajsky Date: Fri, 25 Jul 2014 23:47:46 +0200 Subject: Support OSS --- backends/oss/oss-device.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 backends/oss/oss-device.c (limited to 'backends/oss/oss-device.c') diff --git a/backends/oss/oss-device.c b/backends/oss/oss-device.c new file mode 100644 index 0000000..f33ff57 --- /dev/null +++ b/backends/oss/oss-device.c @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "oss-common.h" +#include "oss-device.h" +#include "oss-stream.h" +#include "oss-stream-control.h" + +#define OSS_DEVICE_ICON "audio-card" + +typedef struct +{ + gchar *name; + gchar *description; + MateMixerStreamControlRole role; + MateMixerStreamFlags flags; +} OssDeviceName; + +static const OssDeviceName const oss_devices[] = { + { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, MATE_MIXER_STREAM_OUTPUT }, + { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, MATE_MIXER_STREAM_OUTPUT }, + { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, MATE_MIXER_STREAM_OUTPUT }, + { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, + { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, MATE_MIXER_STREAM_OUTPUT }, + { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, MATE_MIXER_STREAM_OUTPUT }, + { "line", N_("Line-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, MATE_MIXER_STREAM_INPUT }, + /* Recording monitor */ + { "mix", N_("Mixer"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "pcm2", N_("PCM-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, MATE_MIXER_STREAM_OUTPUT }, + /* Recording level (master input) */ + { "rec", N_("Record"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, MATE_MIXER_STREAM_INPUT }, + { "igain", N_("In-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, + { "ogain", N_("Out-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "line1", N_("Line-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "line2", N_("Line-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "line3", N_("Line-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "dig1", N_("Digital-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, + { "dig2", N_("Digital-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, + { "dig3", N_("Digital-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, + { "phin", N_("Phone-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "phout", N_("Phone-out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_OUTPUT }, + { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "monitor", N_("Monitor"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "depth", N_("3D-depth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "center", N_("3D-center"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "midi", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT } +}; + +struct _OssDevicePrivate +{ + gint fd; + gchar *path; + gchar *name; + gchar *description; + MateMixerStream *input; + MateMixerStream *output; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_DESCRIPTION, + PROP_ICON, + PROP_ACTIVE_PROFILE, + PROP_PATH, + PROP_FD +}; + +static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); + +static void oss_device_class_init (OssDeviceClass *klass); + +static void oss_device_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void oss_device_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void oss_device_init (OssDevice *device); +static void oss_device_finalize (GObject *object); + +G_DEFINE_TYPE_WITH_CODE (OssDevice, oss_device, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE, + mate_mixer_device_interface_init)) + +static const gchar *oss_device_get_name (MateMixerDevice *device); +static const gchar *oss_device_get_description (MateMixerDevice *device); +static const gchar *oss_device_get_icon (MateMixerDevice *device); + +static gboolean read_mixer_devices (OssDevice *device); + +static void +mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) +{ + iface->get_name = oss_device_get_name; + iface->get_description = oss_device_get_description; + iface->get_icon = oss_device_get_icon; +} + +static void +oss_device_class_init (OssDeviceClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = oss_device_finalize; + object_class->get_property = oss_device_get_property; + object_class->set_property = oss_device_set_property; + + g_object_class_install_property (object_class, + PROP_PATH, + g_param_spec_string ("path", + "Path", + "Path to the device", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_FD, + g_param_spec_int ("fd", + "File descriptor", + "File descriptor of the device", + G_MININT, + G_MAXINT, + -1, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_override_property (object_class, PROP_NAME, "name"); + g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); + g_object_class_override_property (object_class, PROP_ICON, "icon"); + g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile"); + + g_type_class_add_private (object_class, sizeof (OssDevicePrivate)); +} + +static void +oss_device_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + OssDevice *device; + + device = OSS_DEVICE (object); + + switch (param_id) { + case PROP_NAME: + g_value_set_string (value, device->priv->name); + break; + case PROP_DESCRIPTION: + g_value_set_string (value, device->priv->description); + break; + case PROP_ICON: + g_value_set_string (value, OSS_DEVICE_ICON); + break; + case PROP_ACTIVE_PROFILE: + /* Not supported */ + g_value_set_object (value, NULL); + break; + case PROP_PATH: + g_value_set_string (value, device->priv->path); + break; + case PROP_FD: + g_value_set_int (value, device->priv->fd); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +oss_device_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + OssDevice *device; + + device = OSS_DEVICE (object); + + switch (param_id) { + case PROP_PATH: + /* Construct-only string */ + device->priv->path = g_strdup (g_value_get_string (value)); + break; + case PROP_FD: + device->priv->fd = dup (g_value_get_int (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +oss_device_init (OssDevice *device) +{ + device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, + OSS_TYPE_DEVICE, + OssDevicePrivate); +} + +static void +oss_device_finalize (GObject *object) +{ + OssDevice *device; + + device = OSS_DEVICE (object); + + g_free (device->priv->name); + g_free (device->priv->description); + + if (device->priv->fd != -1) + g_close (device->priv->fd, NULL); + + G_OBJECT_CLASS (oss_device_parent_class)->finalize (object); +} + +OssDevice * +oss_device_new (const gchar *path, gint fd) +{ + OssDevice *device; + gchar *basename; + + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (OSS_TYPE_DEVICE, + "path", path, + "fd", fd, + NULL); + + basename = g_path_get_basename (path); + + device->priv->name = g_strdup_printf ("oss-%s", basename); + g_free (basename); + + return device; +} + +gboolean +oss_device_read (OssDevice *device) +{ + gchar *name; + gchar *basename; + MateMixerStreamControl *ctl; + + g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); + + // XXX avoid calling this function repeatedly + + g_debug ("Querying device %s (%s)", + device->priv->path, + device->priv->description); + + basename = g_path_get_basename (device->priv->path); + + name = g_strdup_printf ("oss-input-%s", basename); + device->priv->input = MATE_MIXER_STREAM (oss_stream_new (name, + device->priv->description, + MATE_MIXER_STREAM_INPUT)); + g_free (name); + + name = g_strdup_printf ("oss-output-%s", basename); + device->priv->output = MATE_MIXER_STREAM (oss_stream_new (name, + device->priv->description, + MATE_MIXER_STREAM_OUTPUT | + MATE_MIXER_STREAM_PORTS_FIXED)); + g_free (name); + + if (read_mixer_devices (device) == FALSE) + return FALSE; + + // XXX prefer active ports as default if there is no default + + ctl = mate_mixer_stream_get_default_control (device->priv->input); + if (ctl == NULL) { + const GList *list = mate_mixer_stream_list_controls (device->priv->input); + + if (list != NULL) { + ctl = MATE_MIXER_STREAM_CONTROL (list->data); + oss_stream_set_default_control (OSS_STREAM (device->priv->input), + OSS_STREAM_CONTROL (ctl)); + } else + g_clear_object (&device->priv->input); + } + + if (ctl != NULL) + g_debug ("Default input stream control is %s", + mate_mixer_stream_control_get_description (ctl)); + + ctl = mate_mixer_stream_get_default_control (device->priv->output); + if (ctl == NULL) { + const GList *list = mate_mixer_stream_list_controls (device->priv->output); + + if (list != NULL) { + ctl = MATE_MIXER_STREAM_CONTROL (list->data); + oss_stream_set_default_control (OSS_STREAM (device->priv->output), + OSS_STREAM_CONTROL (ctl)); + } else + g_clear_object (&device->priv->output); + } + + if (ctl != NULL) + g_debug ("Default output stream control is %s", + mate_mixer_stream_control_get_description (ctl)); + + return TRUE; +} + +const gchar * +oss_device_get_path (OssDevice *odevice) +{ + g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + + return odevice->priv->path; +} + +MateMixerStream * +oss_device_get_input_stream (OssDevice *odevice) +{ + g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + + return odevice->priv->input; +} + +MateMixerStream * +oss_device_get_output_stream (OssDevice *odevice) +{ + g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + + return odevice->priv->output; +} + +gboolean +oss_device_set_description (OssDevice *odevice, const gchar *description) +{ + g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + + if (g_strcmp0 (odevice->priv->description, description) != 0) { + g_free (odevice->priv->description); + + odevice->priv->description = g_strdup (description); + + g_object_notify (G_OBJECT (odevice), "description"); + return TRUE; + } + return FALSE; +} + +static gboolean +read_mixer_devices (OssDevice *device) +{ + gint devmask, + stereomask, + recmask; + gint ret; + gint i; + OssStreamControl *ctl; + + ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_DEVMASK), &devmask); + if (ret < 0) + goto fail; + ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_STEREODEVS), &stereomask); + if (ret < 0) + goto fail; + ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_RECMASK), &recmask); + if (ret < 0) + goto fail; + + for (i = 0; i < MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES); i++) { + gboolean stereo; + gboolean input = FALSE; + MateMixerPort *port = NULL; + + /* Skip unavailable controls */ + if ((devmask & (1 << i)) == 0) + continue; + + if ((stereomask & (1 << i)) > 0) + stereo = TRUE; + else + stereo = FALSE; + + if (oss_devices[i].flags == MATE_MIXER_STREAM_NO_FLAGS) { + if ((recmask & (1 << i)) > 0) + input = TRUE; + } + if (oss_devices[i].flags == MATE_MIXER_STREAM_INPUT) { + if ((recmask & (1 << i)) == 0) { + g_debug ("Skipping non-input device %s", oss_devices[i].name); + continue; + } + input = TRUE; + } + + ctl = oss_stream_control_new (device->priv->fd, + i, + oss_devices[i].name, + oss_devices[i].description, + stereo); + + if (oss_devices[i].role == MATE_MIXER_STREAM_CONTROL_ROLE_PORT) + port = _mate_mixer_port_new (oss_devices[i].name, + oss_devices[i].description, + NULL, + 0, + 0); + + if (input == TRUE) { + oss_stream_add_control (OSS_STREAM (device->priv->input), ctl); + + if (i == SOUND_MIXER_RECLEV || i == SOUND_MIXER_IGAIN) { + if (i == SOUND_MIXER_RECLEV) { + oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); + } else { + MateMixerStreamControl *defctl; + + defctl = mate_mixer_stream_get_default_control (device->priv->input); + if (defctl == NULL) + oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); + } + } + + if (port != NULL) + oss_stream_add_port (OSS_STREAM (device->priv->input), port); + } else { + oss_stream_add_control (OSS_STREAM (device->priv->output), ctl); + + if (i == SOUND_MIXER_VOLUME) { + oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); + } + else if (i == SOUND_MIXER_PCM) { + MateMixerStreamControl *defctl; + + defctl = mate_mixer_stream_get_default_control (device->priv->output); + if (defctl == NULL) + oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); + } + + if (port != NULL) + oss_stream_add_port (OSS_STREAM (device->priv->output), port); + } + + if (port != NULL) + oss_stream_control_set_port (ctl, port); + + oss_stream_control_set_role (ctl, oss_devices[i].role); + + g_debug ("Added control %s", + mate_mixer_stream_control_get_description (MATE_MIXER_STREAM_CONTROL (ctl))); + + oss_stream_control_update (ctl); + } + return TRUE; + +fail: + g_warning ("Failed to read device %s: %s", + device->priv->path, + g_strerror (errno)); + + return FALSE; +} + +static const gchar * +oss_device_get_name (MateMixerDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + + return OSS_DEVICE (device)->priv->name; +} + +static const gchar * +oss_device_get_description (MateMixerDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + + return OSS_DEVICE (device)->priv->description; +} + +static const gchar * +oss_device_get_icon (MateMixerDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + + return OSS_DEVICE_ICON; +} -- cgit v1.2.1 From 6c6d4239ddc807e922df3874654f99eea291aadb Mon Sep 17 00:00:00 2001 From: Michal Ratajsky Date: Tue, 12 Aug 2014 04:56:55 +0200 Subject: Add ALSA, improve OSS and remove OSS4 --- backends/oss/oss-device.c | 609 +++++++++++++++++++--------------------------- 1 file changed, 244 insertions(+), 365 deletions(-) (limited to 'backends/oss/oss-device.c') diff --git a/backends/oss/oss-device.c b/backends/oss/oss-device.c index f33ff57..cf51705 100644 --- a/backends/oss/oss-device.c +++ b/backends/oss/oss-device.c @@ -22,12 +22,8 @@ #include #include -#include -#include -#include -#include -#include -#include +#include +#include #include "oss-common.h" #include "oss-device.h" @@ -36,490 +32,373 @@ #define OSS_DEVICE_ICON "audio-card" +typedef enum +{ + OSS_DEV_ANY, + OSS_DEV_INPUT, + OSS_DEV_OUTPUT +} OssDevType; + typedef struct { gchar *name; - gchar *description; + gchar *label; MateMixerStreamControlRole role; - MateMixerStreamFlags flags; -} OssDeviceName; - -static const OssDeviceName const oss_devices[] = { - { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, MATE_MIXER_STREAM_OUTPUT }, - { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, MATE_MIXER_STREAM_OUTPUT }, - { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, MATE_MIXER_STREAM_OUTPUT }, - { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, - { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, MATE_MIXER_STREAM_OUTPUT }, - { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, MATE_MIXER_STREAM_OUTPUT }, - { "line", N_("Line-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, MATE_MIXER_STREAM_INPUT }, + 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, MATE_MIXER_STREAM_OUTPUT }, - { "pcm2", N_("PCM-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, MATE_MIXER_STREAM_OUTPUT }, + { "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, MATE_MIXER_STREAM_INPUT }, - { "igain", N_("In-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, - { "ogain", N_("Out-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, - { "line1", N_("Line-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "line2", N_("Line-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "line3", N_("Line-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "dig1", N_("Digital-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, - { "dig2", N_("Digital-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, - { "dig3", N_("Digital-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, - { "phin", N_("Phone-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "phout", N_("Phone-out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_OUTPUT }, - { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "monitor", N_("Monitor"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, - { "depth", N_("3D-depth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, - { "center", N_("3D-center"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, - { "midi", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_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; - gchar *name; - gchar *description; - MateMixerStream *input; - MateMixerStream *output; -}; - -enum { - PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_ICON, - PROP_ACTIVE_PROFILE, - PROP_PATH, - PROP_FD + gint fd; + gchar *path; + gint devmask; + gint stereodevs; + gint recmask; + gint recsrc; + OssStream *input; + OssStream *output; }; -static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); - static void oss_device_class_init (OssDeviceClass *klass); - -static void oss_device_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void oss_device_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); - static void oss_device_init (OssDevice *device); +static void oss_device_dispose (GObject *object); static void oss_device_finalize (GObject *object); -G_DEFINE_TYPE_WITH_CODE (OssDevice, oss_device, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE, - mate_mixer_device_interface_init)) +G_DEFINE_TYPE (OssDevice, oss_device, MATE_MIXER_TYPE_DEVICE) -static const gchar *oss_device_get_name (MateMixerDevice *device); -static const gchar *oss_device_get_description (MateMixerDevice *device); -static const gchar *oss_device_get_icon (MateMixerDevice *device); +static GList * oss_device_list_streams (MateMixerDevice *device); -static gboolean read_mixer_devices (OssDevice *device); +static gboolean read_mixer_devices (OssDevice *device); -static void -mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) -{ - iface->get_name = oss_device_get_name; - iface->get_description = oss_device_get_description; - iface->get_icon = oss_device_get_icon; -} +static gboolean set_stream_default_control (OssStream *stream, + OssStreamControl *control, + gboolean force); static void oss_device_class_init (OssDeviceClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerDeviceClass *device_class; object_class = G_OBJECT_CLASS (klass); - object_class->finalize = oss_device_finalize; - object_class->get_property = oss_device_get_property; - object_class->set_property = oss_device_set_property; - - g_object_class_install_property (object_class, - PROP_PATH, - g_param_spec_string ("path", - "Path", - "Path to the device", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_FD, - g_param_spec_int ("fd", - "File descriptor", - "File descriptor of the device", - G_MININT, - G_MAXINT, - -1, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_override_property (object_class, PROP_NAME, "name"); - g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); - g_object_class_override_property (object_class, PROP_ICON, "icon"); - g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile"); + 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_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) +oss_device_init (OssDevice *device) { - OssDevice *device; - - device = OSS_DEVICE (object); - - switch (param_id) { - case PROP_NAME: - g_value_set_string (value, device->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, device->priv->description); - break; - case PROP_ICON: - g_value_set_string (value, OSS_DEVICE_ICON); - break; - case PROP_ACTIVE_PROFILE: - /* Not supported */ - g_value_set_object (value, NULL); - break; - case PROP_PATH: - g_value_set_string (value, device->priv->path); - break; - case PROP_FD: - g_value_set_int (value, device->priv->fd); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } + device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, + OSS_TYPE_DEVICE, + OssDevicePrivate); } static void -oss_device_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) +oss_device_dispose (GObject *object) { OssDevice *device; device = OSS_DEVICE (object); - switch (param_id) { - case PROP_PATH: - /* Construct-only string */ - device->priv->path = g_strdup (g_value_get_string (value)); - break; - case PROP_FD: - device->priv->fd = dup (g_value_get_int (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} + g_clear_object (&device->priv->input); + g_clear_object (&device->priv->output); -static void -oss_device_init (OssDevice *device) -{ - device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, - OSS_TYPE_DEVICE, - OssDevicePrivate); + G_OBJECT_CLASS (oss_device_parent_class)->dispose (object); } static void oss_device_finalize (GObject *object) { - OssDevice *device; - - device = OSS_DEVICE (object); - - g_free (device->priv->name); - g_free (device->priv->description); + OssDevice *device = OSS_DEVICE (object); - if (device->priv->fd != -1) - g_close (device->priv->fd, NULL); + close (device->priv->fd); G_OBJECT_CLASS (oss_device_parent_class)->finalize (object); } OssDevice * -oss_device_new (const gchar *path, gint fd) +oss_device_new (const gchar *name, const gchar *label, const gchar *path, gint fd) { OssDevice *device; - gchar *basename; + gchar *stream_name; - g_return_val_if_fail (path != NULL, NULL); + 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, - "path", path, - "fd", fd, + "name", name, + "label", label, + "icon", OSS_DEVICE_ICON, NULL); - basename = g_path_get_basename (path); + device->priv->fd = dup (fd); + device->priv->path = g_strdup (path); - device->priv->name = g_strdup_printf ("oss-%s", basename); - g_free (basename); + 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_read (OssDevice *device) +oss_device_open (OssDevice *device) { - gchar *name; - gchar *basename; - MateMixerStreamControl *ctl; + gint ret; g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); - // XXX avoid calling this function repeatedly - - g_debug ("Querying device %s (%s)", + g_debug ("Opening device %s (%s)", device->priv->path, - device->priv->description); - - basename = g_path_get_basename (device->priv->path); - - name = g_strdup_printf ("oss-input-%s", basename); - device->priv->input = MATE_MIXER_STREAM (oss_stream_new (name, - device->priv->description, - MATE_MIXER_STREAM_INPUT)); - g_free (name); - - name = g_strdup_printf ("oss-output-%s", basename); - device->priv->output = MATE_MIXER_STREAM (oss_stream_new (name, - device->priv->description, - MATE_MIXER_STREAM_OUTPUT | - MATE_MIXER_STREAM_PORTS_FIXED)); - g_free (name); - - if (read_mixer_devices (device) == FALSE) - return FALSE; - - // XXX prefer active ports as default if there is no default - - ctl = mate_mixer_stream_get_default_control (device->priv->input); - if (ctl == NULL) { - const GList *list = mate_mixer_stream_list_controls (device->priv->input); - - if (list != NULL) { - ctl = MATE_MIXER_STREAM_CONTROL (list->data); - oss_stream_set_default_control (OSS_STREAM (device->priv->input), - OSS_STREAM_CONTROL (ctl)); - } else - g_clear_object (&device->priv->input); + 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 (ctl != NULL) + if (control != NULL) g_debug ("Default input stream control is %s", - mate_mixer_stream_control_get_description (ctl)); - - ctl = mate_mixer_stream_get_default_control (device->priv->output); - if (ctl == NULL) { - const GList *list = mate_mixer_stream_list_controls (device->priv->output); - - if (list != NULL) { - ctl = MATE_MIXER_STREAM_CONTROL (list->data); - oss_stream_set_default_control (OSS_STREAM (device->priv->output), - OSS_STREAM_CONTROL (ctl)); - } else - g_clear_object (&device->priv->output); + 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 (ctl != NULL) + if (control != NULL) g_debug ("Default output stream control is %s", - mate_mixer_stream_control_get_description (ctl)); + 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 *odevice) +oss_device_get_path (OssDevice *device) { - g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - return odevice->priv->path; + return device->priv->path; } -MateMixerStream * -oss_device_get_input_stream (OssDevice *odevice) +OssStream * +oss_device_get_input_stream (OssDevice *device) { - g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - return odevice->priv->input; + return device->priv->input; } -MateMixerStream * -oss_device_get_output_stream (OssDevice *odevice) +OssStream * +oss_device_get_output_stream (OssDevice *device) { - g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - return odevice->priv->output; + return device->priv->output; } -gboolean -oss_device_set_description (OssDevice *odevice, const gchar *description) +static GList * +oss_device_list_streams (MateMixerDevice *mmd) { - g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + OssDevice *device; + GList *list = NULL; - if (g_strcmp0 (odevice->priv->description, description) != 0) { - g_free (odevice->priv->description); + g_return_val_if_fail (OSS_IS_DEVICE (mmd), NULL); - odevice->priv->description = g_strdup (description); + device = OSS_DEVICE (mmd); - g_object_notify (G_OBJECT (odevice), "description"); - return TRUE; - } - return FALSE; + 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 devmask, - stereomask, - recmask; - gint ret; - gint i; - OssStreamControl *ctl; - - ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_DEVMASK), &devmask); - if (ret < 0) - goto fail; - ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_STEREODEVS), &stereomask); - if (ret < 0) - goto fail; - ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_RECMASK), &recmask); - if (ret < 0) - goto fail; + gint i; - for (i = 0; i < MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES); i++) { - gboolean stereo; - gboolean input = FALSE; - MateMixerPort *port = NULL; + for (i = 0; i < OSS_N_DEVICES; i++) { + OssStreamControl *control; + gboolean input = FALSE; /* Skip unavailable controls */ - if ((devmask & (1 << i)) == 0) + if (OSS_MASK_HAS_DEVICE (device->priv->devmask, i) == FALSE) continue; - if ((stereomask & (1 << i)) > 0) - stereo = TRUE; - else - stereo = FALSE; - - if (oss_devices[i].flags == MATE_MIXER_STREAM_NO_FLAGS) { - if ((recmask & (1 << i)) > 0) - input = TRUE; + if (oss_devices[i].type == OSS_DEV_ANY) { + input = OSS_MASK_HAS_DEVICE (device->priv->recmask, i); } - if (oss_devices[i].flags == MATE_MIXER_STREAM_INPUT) { - if ((recmask & (1 << i)) == 0) { - g_debug ("Skipping non-input device %s", oss_devices[i].name); - continue; - } + else if (oss_devices[i].type == OSS_DEV_INPUT) { input = TRUE; } - ctl = oss_stream_control_new (device->priv->fd, - i, - oss_devices[i].name, - oss_devices[i].description, - stereo); - - if (oss_devices[i].role == MATE_MIXER_STREAM_CONTROL_ROLE_PORT) - port = _mate_mixer_port_new (oss_devices[i].name, - oss_devices[i].description, - NULL, - 0, - 0); + 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), ctl); + oss_stream_add_control (OSS_STREAM (device->priv->input), control); if (i == SOUND_MIXER_RECLEV || i == SOUND_MIXER_IGAIN) { - if (i == SOUND_MIXER_RECLEV) { - oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); - } else { - MateMixerStreamControl *defctl; - - defctl = mate_mixer_stream_get_default_control (device->priv->input); - if (defctl == NULL) - oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); - } + 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); } - - if (port != NULL) - oss_stream_add_port (OSS_STREAM (device->priv->input), port); } else { - oss_stream_add_control (OSS_STREAM (device->priv->output), ctl); - - if (i == SOUND_MIXER_VOLUME) { - oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); - } - else if (i == SOUND_MIXER_PCM) { - MateMixerStreamControl *defctl; - - defctl = mate_mixer_stream_get_default_control (device->priv->output); - if (defctl == NULL) - oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); + 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); } - - if (port != NULL) - oss_stream_add_port (OSS_STREAM (device->priv->output), port); } - if (port != NULL) - oss_stream_control_set_port (ctl, port); - - oss_stream_control_set_role (ctl, oss_devices[i].role); - g_debug ("Added control %s", - mate_mixer_stream_control_get_description (MATE_MIXER_STREAM_CONTROL (ctl))); + mate_mixer_stream_control_get_label (MATE_MIXER_STREAM_CONTROL (control))); - oss_stream_control_update (ctl); + oss_stream_control_update (control); } return TRUE; - -fail: - g_warning ("Failed to read device %s: %s", - device->priv->path, - g_strerror (errno)); - - return FALSE; } -static const gchar * -oss_device_get_name (MateMixerDevice *device) -{ - g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - - return OSS_DEVICE (device)->priv->name; -} - -static const gchar * -oss_device_get_description (MateMixerDevice *device) -{ - g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - - return OSS_DEVICE (device)->priv->description; -} - -static const gchar * -oss_device_get_icon (MateMixerDevice *device) +static gboolean +set_stream_default_control (OssStream *stream, OssStreamControl *control, gboolean force) { - g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + MateMixerStreamControl *current; - return OSS_DEVICE_ICON; + 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; } -- cgit v1.2.1