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-backend.c | 699 +++++++++++++++++++------------------- backends/oss/oss-backend.h | 5 +- backends/oss/oss-common.h | 1 + backends/oss/oss-device.c | 609 +++++++++++++-------------------- backends/oss/oss-device.h | 28 +- backends/oss/oss-stream-control.c | 527 +++++++++++----------------- backends/oss/oss-stream-control.h | 26 +- backends/oss/oss-stream.c | 266 ++++----------- backends/oss/oss-stream.h | 25 +- 9 files changed, 890 insertions(+), 1296 deletions(-) (limited to 'backends/oss') diff --git a/backends/oss/oss-backend.c b/backends/oss/oss-backend.c index 6e058f8..2b5eca7 100644 --- a/backends/oss/oss-backend.c +++ b/backends/oss/oss-backend.c @@ -19,91 +19,78 @@ #include #include #include -#include #include -#include #include -#include +#include -#include -#include -#include -#include +#include +#include #include "oss-backend.h" #include "oss-common.h" #include "oss-device.h" +#include "oss-stream.h" #define BACKEND_NAME "OSS" #define BACKEND_PRIORITY 9 -#define PATH_SNDSTAT "/dev/sndstat" +#if !defined(__linux__) && !defined(__NetBSD__) && !defined(__OpenBSD__) + /* At least on systems based on FreeBSD we will need to read device names + * from the sndstat file, but avoid even trying that on systems where this + * is not needed and the file is not present */ +#define OSS_PATH_SNDSTAT "/dev/sndstat" +#endif + +#define OSS_MAX_DEVICES 32 struct _OssBackendPrivate { - GFile *sndstat; - GFile *dev; - GFileMonitor *dev_monitor; - GHashTable *devices; - GHashTable *streams; - MateMixerStream *default_input; - MateMixerStream *default_output; - MateMixerState state; -}; - -enum { - PROP_0, - PROP_STATE, - PROP_DEFAULT_INPUT, - PROP_DEFAULT_OUTPUT + gchar *default_device; + GSource *timeout_source; + GHashTable *devices; }; -static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); - static void oss_backend_class_init (OssBackendClass *klass); static void oss_backend_class_finalize (OssBackendClass *klass); - -static void oss_backend_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); - static void oss_backend_init (OssBackend *oss); static void oss_backend_dispose (GObject *object); static void oss_backend_finalize (GObject *object); -G_DEFINE_DYNAMIC_TYPE_EXTENDED (OssBackend, oss_backend, - G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, - mate_mixer_backend_interface_init)) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_DYNAMIC_TYPE (OssBackend, oss_backend, MATE_MIXER_TYPE_BACKEND) +#pragma clang diagnostic pop + +static gboolean oss_backend_open (MateMixerBackend *backend); +static void oss_backend_close (MateMixerBackend *backend); +static GList * oss_backend_list_devices (MateMixerBackend *backend); +static GList * oss_backend_list_streams (MateMixerBackend *backend); -static gboolean oss_backend_open (MateMixerBackend *backend); -static void oss_backend_close (MateMixerBackend *backend); -static GList * oss_backend_list_devices (MateMixerBackend *backend); -static GList * oss_backend_list_streams (MateMixerBackend *backend); +static gboolean read_devices (OssBackend *oss); -static void change_state (OssBackend *oss, - MateMixerState state); +static gboolean read_device (OssBackend *oss, + const gchar *path, + gboolean *added); -static void on_dev_monitor_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - OssBackend *oss); +static gchar * read_device_label (OssBackend *oss, + const gchar *path, + gint fd); -static gboolean read_device (OssBackend *oss, - const gchar *path); +static gchar * read_device_label_sndstat (OssBackend *oss, + const gchar *sndstat, + const gchar *path, + guint index) G_GNUC_UNUSED; -static gchar * read_device_description (OssBackend *oss, - const gchar *path, - gint fd); -static gchar * read_device_sndstat_description (const gchar *path, - GFileInputStream *input); +static void add_device (OssBackend *oss, + OssDevice *device); +static void remove_device (OssBackend *oss, + OssDevice *device); -static void add_device (OssBackend *oss, - OssDevice *device); -static void remove_device (OssBackend *oss, - OssDevice *device); +static void remove_stream (OssBackend *oss, + const gchar *name); + +static void select_default_input_stream (OssBackend *oss); +static void select_default_output_stream (OssBackend *oss); static MateMixerBackendInfo info; @@ -123,65 +110,31 @@ const MateMixerBackendInfo *backend_module_get_info (void) return &info; } -static void -mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) -{ - iface->open = oss_backend_open; - iface->close = oss_backend_close; - iface->list_devices = oss_backend_list_devices; - iface->list_streams = oss_backend_list_streams; -} - static void oss_backend_class_init (OssBackendClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerBackendClass *backend_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = oss_backend_dispose; - object_class->finalize = oss_backend_finalize; - object_class->get_property = oss_backend_get_property; + object_class->dispose = oss_backend_dispose; + object_class->finalize = oss_backend_finalize; - g_object_class_override_property (object_class, PROP_STATE, "state"); - g_object_class_override_property (object_class, PROP_DEFAULT_INPUT, "default-input"); - g_object_class_override_property (object_class, PROP_DEFAULT_OUTPUT, "default-output"); + backend_class = MATE_MIXER_BACKEND_CLASS (klass); + backend_class->open = oss_backend_open; + backend_class->close = oss_backend_close; + backend_class->list_devices = oss_backend_list_devices; + backend_class->list_streams = oss_backend_list_streams; g_type_class_add_private (object_class, sizeof (OssBackendPrivate)); } -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE() */ static void oss_backend_class_finalize (OssBackendClass *klass) { } -static void -oss_backend_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - OssBackend *oss; - - oss = OSS_BACKEND (object); - - switch (param_id) { - case PROP_STATE: - g_value_set_enum (value, oss->priv->state); - break; - case PROP_DEFAULT_INPUT: - g_value_set_object (value, oss->priv->default_input); - break; - case PROP_DEFAULT_OUTPUT: - g_value_set_object (value, oss->priv->default_output); - break; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - static void oss_backend_init (OssBackend *oss) { @@ -193,17 +146,21 @@ oss_backend_init (OssBackend *oss) g_str_equal, g_free, g_object_unref); - - oss->priv->streams = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); } static void oss_backend_dispose (GObject *object) { - oss_backend_close (MATE_MIXER_BACKEND (object)); + MateMixerBackend *backend; + MateMixerState state; + + backend = MATE_MIXER_BACKEND (object); + + state = mate_mixer_backend_get_state (backend); + if (state != MATE_MIXER_STATE_IDLE) + oss_backend_close (backend); + + G_OBJECT_CLASS (oss_backend_parent_class)->dispose (object); } static void @@ -214,61 +171,34 @@ oss_backend_finalize (GObject *object) oss = OSS_BACKEND (object); g_hash_table_destroy (oss->priv->devices); - g_hash_table_destroy (oss->priv->streams); + + G_OBJECT_CLASS (oss_backend_parent_class)->finalize (object); } static gboolean oss_backend_open (MateMixerBackend *backend) { OssBackend *oss; - GError *error = NULL; - gint i; g_return_val_if_fail (OSS_IS_BACKEND (backend), FALSE); oss = OSS_BACKEND (backend); - /* Monitor changes on /dev to catch hot-(un)plugged devices */ - // XXX works on linux, doesn't on freebsd, what to do on netbsd/openbsd? - oss->priv->dev = g_file_new_for_path ("/dev"); - oss->priv->dev_monitor = g_file_monitor_directory (oss->priv->dev, - G_FILE_MONITOR_NONE, - NULL, - &error); - if (oss->priv->dev_monitor != NULL) { - g_signal_connect (G_OBJECT (oss->priv->dev_monitor), - "changed", - G_CALLBACK (on_dev_monitor_changed), - oss); - } else { - g_debug ("Failed to monitor /dev: %s", error->message); - g_error_free (error); - } - -#if !defined(__linux__) && !defined(__NetBSD__) && !defined(__OpenBSD__) - /* At least on systems based on FreeBSD we will need to read devices names - * from the sndstat file, but avoid even trying that on systems where this - * is not needed and the file is not present */ - oss->priv->sndstat = g_file_new_for_path (PATH_SNDSTAT); -#endif - - for (i = 0; i < 5; i++) { - /* According to the OSS documentation the mixer devices are - * /dev/mixer0 - /dev/mixer4, of course some systems create them - * dynamically but this approach should be good enough */ - gchar *path = g_strdup_printf ("/dev/mixer%i", i); - - if (read_device (oss, path) == FALSE && i == 0) { - /* For the first mixer device check also /dev/mixer, but it - * might be a symlink to a real mixer device */ - if (g_file_test ("/dev/mixer", G_FILE_TEST_IS_SYMLINK) == FALSE) - read_device (oss, "/dev/mixer"); - } - - g_free (path); - } - - change_state (oss, MATE_MIXER_STATE_READY); + /* Discover added or removed OSS devices every second */ + oss->priv->timeout_source = g_timeout_source_new_seconds (1); + g_source_set_callback (oss->priv->timeout_source, + (GSourceFunc) read_devices, + oss, + NULL); + g_source_attach (oss->priv->timeout_source, + g_main_context_get_thread_default ()); + + /* Read the initial list of devices so we have some starting point, there + * isn't really a way to detect errors here, failing to add a device may + * be a device-related problem so make the backend always open successfully */ + read_devices (oss); + + _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_READY); return TRUE; } @@ -281,15 +211,13 @@ oss_backend_close (MateMixerBackend *backend) oss = OSS_BACKEND (backend); - g_clear_object (&oss->priv->default_input); - g_clear_object (&oss->priv->default_output); - - g_hash_table_remove_all (oss->priv->streams); + g_source_destroy (oss->priv->timeout_source); g_hash_table_remove_all (oss->priv->devices); - g_clear_object (&oss->priv->dev_monitor); - g_clear_object (&oss->priv->dev); - g_clear_object (&oss->priv->sndstat); + g_free (oss->priv->default_device); + oss->priv->default_device = NULL; + + _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE); } static GList * @@ -299,306 +227,361 @@ oss_backend_list_devices (MateMixerBackend *backend) g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ + /* Convert the hash table to a linked list, this list is expected to be + * cached in the main library */ list = g_hash_table_get_values (OSS_BACKEND (backend)->priv->devices); - if (list != NULL) { + if (list != NULL) g_list_foreach (list, (GFunc) g_object_ref, NULL); - return list; - } - return NULL; + return list; } static GList * oss_backend_list_streams (MateMixerBackend *backend) { - GList *list; + OssBackend *oss; + GHashTableIter iter; + gpointer value; + GList *list = NULL; g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ - list = g_hash_table_get_values (OSS_BACKEND (backend)->priv->streams); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); + oss = OSS_BACKEND (backend); - return list; + /* We don't keep a list or hash table of all streams here, instead walk + * through the list of devices and create the list manually, each device + * has at most one input and one output stream */ + g_hash_table_iter_init (&iter, oss->priv->devices); + + while (g_hash_table_iter_next (&iter, NULL, &value)) { + OssDevice *device = OSS_DEVICE (value); + OssStream *stream; + + stream = oss_device_get_output_stream (device); + if (stream != NULL) + list = g_list_prepend (list, stream); + stream = oss_device_get_input_stream (device); + if (stream != NULL) + list = g_list_prepend (list, stream); } - return NULL; -} - -static void -change_state (OssBackend *oss, MateMixerState state) -{ - if (oss->priv->state == state) - return; - oss->priv->state = state; + if (list != NULL) + g_list_foreach (list, (GFunc) g_object_ref, NULL); - g_object_notify (G_OBJECT (oss), "state"); + return list; } -static void -on_dev_monitor_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - OssBackend *oss) +static gboolean +read_devices (OssBackend *oss) { - gchar *path; + gint i; + gboolean added = FALSE; - if (event_type != G_FILE_MONITOR_EVENT_CREATED && - event_type != G_FILE_MONITOR_EVENT_DELETED) - return; + for (i = 0; i < OSS_MAX_DEVICES; i++) { + gboolean added_current; + gchar *path = g_strdup_printf ("/dev/mixer%i", i); - path = g_file_get_path (file); + /* On recent FreeBSD both /dev/mixer and /dev/mixer0 point to the same + * mixer device, on NetBSD and OpenBSD /dev/mixer is a symlink to one + * of the real mixer device nodes, on Linux /dev/mixer is the first + * device and /dev/mixer1 is the second device. + * Handle all of these cases by trying /dev/mixer if /dev/mixer0 fails */ + if (read_device (oss, path, &added_current) == FALSE && i == 0) + read_device (oss, "/dev/mixer", &added_current); + + if (added_current) + added = TRUE; - /* Only handle creation and deletion of mixer devices */ - if (g_str_has_prefix (path, "/dev/mixer") == FALSE) { - g_free (path); - return; - } - if (strcmp (path, "/dev/mixer") == 0 && - g_file_test (path, G_FILE_TEST_IS_SYMLINK) == TRUE) { g_free (path); - return; } - if (event_type == G_FILE_MONITOR_EVENT_DELETED) { - OssDevice *device; - - device = g_hash_table_lookup (oss->priv->devices, path); - if (device != NULL) - remove_device (oss, device); - } else - read_device (oss, path); - - g_free (path); + /* If any card has been added, make sure we have the most suitable default + * input and output streams */ + if (added == TRUE) { + select_default_input_stream (oss); + select_default_output_stream (oss); + } + return G_SOURCE_CONTINUE; } static gboolean -read_device (OssBackend *oss, const gchar *path) +read_device (OssBackend *oss, const gchar *path, gboolean *added) { - OssDevice *device; - gint fd; - gboolean ret; - const gchar *description; + OssDevice *device; + gint fd; + gchar *bname; + gchar *label; - /* We assume that the name and capabilities of a device do not change */ device = g_hash_table_lookup (oss->priv->devices, path); - if (G_UNLIKELY (device != NULL)) { - g_debug ("Attempt to re-read already know device %s", path); - return TRUE; - } + *added = FALSE; fd = g_open (path, O_RDWR, 0); if (fd == -1) { if (errno != ENOENT && errno != ENXIO) g_debug ("%s: %s", path, g_strerror (errno)); + + if (device != NULL) + remove_device (oss, device); return FALSE; } - device = oss_device_new (path, fd); + /* Don't proceed if the device is already known, opening the device was + * still tested to be absolutely sure that the device is removed it case + * it has disappeared, but normally the device's polling facility should + * handle this by itself */ + if (device != NULL) { + close (fd); + return TRUE; + } - description = read_device_description (oss, path, fd); - if (description != NULL) - oss_device_set_description (device, description); - else - oss_device_set_description (device, _("Unknown device")); + bname = g_path_get_basename (path); + label = read_device_label (oss, path, fd); - /* Close the descriptor as the device should dup it if it intends - * to keep it */ - g_close (fd, NULL); + device = oss_device_new (bname, label, path, fd); + g_free (bname); + g_free (label); - ret = oss_device_read (device); - if (ret == TRUE) + close (fd); + + if ((*added = oss_device_open (device)) == TRUE) add_device (oss, device); g_object_unref (device); - return ret; + return *added; } static gchar * -read_device_description (OssBackend *oss, const gchar *path, gint fd) +read_device_label (OssBackend *oss, const gchar *path, gint fd) { - struct mixer_info info; + guint index; - if (ioctl (fd, SOUND_MIXER_INFO, &info) == 0) { - /* Prefer device name supplied by the system, but this fails on FreeBSD */ - return g_strdup (info.name); - } +#ifdef SOUND_MIXER_INFO + do { + struct mixer_info info; - /* Reading the sndstat file is a bit desperate, but seem to be the only - * way to determine the name of the sound card on FreeBSD, it also has an - * advantage in being able to find out the default one */ - if (oss->priv->sndstat != NULL) { - GError *error = NULL; - GFileInputStream *input = g_file_read (oss->priv->sndstat, NULL, &error); - - if (input == NULL) { - /* The file can only be open by one application, otherwise the call - * fails with EBUSY */ - if (error->code == G_IO_ERROR_BUSY) { - g_debug ("Delayed reading %s as it is busy", PATH_SNDSTAT); - // XXX use timeout and try again - } else { - if (error->code == G_IO_ERROR_NOT_FOUND) - g_debug ("Device description is unknown as %s does not exist", - PATH_SNDSTAT); - else - g_debug ("%s: %s", PATH_SNDSTAT, error->message); + /* Prefer device name supplied by the system, but this calls fails + * with EINVAL on FreeBSD */ + if (ioctl (fd, SOUND_MIXER_INFO, &info) == 0) + return g_strdup (info.name); + } while (0); +#endif - g_clear_object (&oss->priv->sndstat); - } - g_error_free (error); - } else - return read_device_sndstat_description (path, input); - } + index = (guint) g_ascii_strtoull (path + sizeof ("/dev/mixer") - 1, + NULL, + 10); +#ifdef OSS_PATH_SNDSTAT + /* If the ioctl doesn't succeed, assume that the mixer device number + * matches the pcm number in the sndstat file, this is a bit desperate, but + * it should be correct on FreeBSD */ + do { + gchar *label; + + label = read_device_label_sndstat (oss, OSS_PATH_SNDSTAT, path, index); + if (label != NULL) + return label; + } while (0); +#endif - return NULL; + return g_strdup_printf (_("OSS Mixer %d"), index); } static gchar * -read_device_sndstat_description (const gchar *path, GFileInputStream *input) +read_device_label_sndstat (OssBackend *oss, + const gchar *sndstat, + const gchar *path, + guint index) { - gchar *line; - gchar *prefix; - gchar *description = NULL; - GDataInputStream *stream; - - if (G_UNLIKELY (g_str_has_prefix (path, "/dev/mixer")) == FALSE) { - g_warn_if_reached (); + FILE *fp; + gchar *label = NULL; + gchar *prefix; + gchar line[512]; + + fp = g_fopen (sndstat, "r"); + if (fp == NULL) { + g_debug ("Failed to open %s: %s", sndstat, g_strerror (errno)); return NULL; } - /* We assume that the mixer device number matches the pcm number in the - * sndstat file, this is a bit desperate, but it seems to do the - * right thing in practice */ - prefix = g_strdup_printf ("pcm%u: ", - (guint) g_ascii_strtoull (path + sizeof ("/dev/mixer") - 1, - NULL, - 10)); - - stream = g_data_input_stream_new (G_INPUT_STREAM (input)); - - while (TRUE) { - GError *error = NULL; - gchar *p; - - line = g_data_input_stream_read_line (stream, NULL, NULL, &error); - if (line == NULL) { - if (error != NULL) { - g_warning ("%s: %s", path, error->message); - g_error_free (error); - } - break; - } + /* Example line: + * pcm0: (play) default */ + prefix = g_strdup_printf ("pcm%u: ", index); + + while (fgets (line, sizeof (line), fp) != NULL) { + gchar *p; if (g_str_has_prefix (line, prefix) == FALSE) continue; - /* Example line: - * pcm0: (play) default */ p = strchr (line, '<'); if (p != NULL && *p && *(++p)) { gchar *end = strchr (p, '>'); - if (end != NULL) - description = g_strndup (p, end - p); - } + if (end != NULL) { + label = g_strndup (p, end - p); - // XXX we can also read "default" at the end of the line - // XXX http://ashish.is.lostca.se/2011/05/23/default-sound-device-in-freebsd/ - if (g_str_has_suffix (line, "default") || - g_str_has_suffix (line, "default)")) - ; + /* Normally the default OSS device is /dev/dsp, but on FreeBSD + * /dev/dsp doesn't physically exist on the filesystem, but is + * managed by the kernel according to the user-settable default + * device, in sndstat the default card definition ends with the + * word "default" */ + if (g_str_has_suffix (line, "default")) { + g_free (oss->priv->default_device); - if (description != NULL) + oss->priv->default_device = g_strdup (path); + } + } else { + g_debug ("Failed to read sndstat line: %s", line); + } break; + } } - g_object_unref (stream); g_free (prefix); + fclose (fp); - return description; + return label; } static void add_device (OssBackend *oss, OssDevice *device) { - MateMixerStream *stream; + const gchar *name; + + name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); - /* Add device, file path is used as the key rather than the name, because - * the name is not known until an OssDevice instance is created */ g_hash_table_insert (oss->priv->devices, g_strdup (oss_device_get_path (device)), g_object_ref (device)); - g_signal_emit_by_name (G_OBJECT (oss), - "device-added", - mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); - - /* Add streams if they exist */ - stream = oss_device_get_input_stream (device); - if (stream != NULL) { - g_hash_table_insert (oss->priv->streams, - g_strdup (mate_mixer_stream_get_name (stream)), - g_object_ref (stream)); - - g_signal_emit_by_name (G_OBJECT (oss), - "stream-added", - mate_mixer_stream_get_name (stream)); - } + // XXX make device emit it when closed + g_signal_connect_swapped (G_OBJECT (device), + "stream-removed", + G_CALLBACK (remove_stream), + oss); - stream = oss_device_get_output_stream (device); - if (stream != NULL) { - g_hash_table_insert (oss->priv->streams, - g_strdup (mate_mixer_stream_get_name (stream)), - g_object_ref (stream)); + g_signal_emit_by_name (G_OBJECT (oss), "device-added", name); - g_signal_emit_by_name (G_OBJECT (oss), - "stream-added", - mate_mixer_stream_get_name (stream)); - } + oss_device_load (device); } static void remove_device (OssBackend *oss, OssDevice *device) +{ + const gchar *name; + const gchar *path; + + path = oss_device_get_path (device); + name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + + g_signal_handlers_disconnect_by_func (G_OBJECT (device), + G_CALLBACK (remove_stream), + oss); + + // XXX close the device and make it remove streams + g_hash_table_remove (oss->priv->devices, path); + + g_signal_emit_by_name (G_OBJECT (oss), + "device-removed", + name); +} + +static void +remove_stream (OssBackend *oss, const gchar *name) { MateMixerStream *stream; - const gchar *path; - /* Remove the device streams first as they are a part of the device */ - stream = oss_device_get_input_stream (device); - if (stream != NULL) { - const gchar *name = mate_mixer_stream_get_name (stream); + stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (oss)); + + // XXX see if the change happens after stream is removed or before + if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) + select_default_input_stream (oss); + + stream = mate_mixer_backend_get_default_output_stream (MATE_MIXER_BACKEND (oss)); + + if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) + select_default_output_stream (oss); +} - g_hash_table_remove (oss->priv->streams, name); - g_signal_emit_by_name (G_OBJECT (oss), - "stream-removed", - name); +static void +select_default_input_stream (OssBackend *oss) +{ + OssDevice *device = NULL; + OssStream *stream; + gint i; + + /* Always prefer stream in the "default" device */ + if (oss->priv->default_device != NULL) + device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device); + if (device != NULL) { + stream = oss_device_get_input_stream (device); + if (stream != NULL) { + _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + return; + } } - stream = oss_device_get_output_stream (device); - if (stream != NULL) { - const gchar *name = mate_mixer_stream_get_name (stream); + for (i = 0; i < OSS_MAX_DEVICES; i++) { + gchar *path = g_strdup_printf ("/dev/mixer%i", i); - g_hash_table_remove (oss->priv->streams, stream); - g_signal_emit_by_name (G_OBJECT (oss), - "stream-removed", - name); + device = g_hash_table_lookup (oss->priv->devices, path); + if (device == NULL && i == 0) + device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer"); + + if (device != NULL) { + stream = oss_device_get_input_stream (device); + if (stream != NULL) { + _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + g_free (path); + return; + } + } + g_free (path); } - path = oss_device_get_path (device); + /* In the worst case unset the default stream */ + _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), NULL); +} - /* Remove the device */ - g_object_ref (device); +static void +select_default_output_stream (OssBackend *oss) +{ + OssDevice *device = NULL; + OssStream *stream; + gint i; + + /* Always prefer stream in the "default" device */ + if (oss->priv->default_device != NULL) + device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device); + if (device != NULL) { + stream = oss_device_get_output_stream (device); + if (stream != NULL) { + _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + return; + } + } - g_hash_table_remove (oss->priv->devices, path); - g_signal_emit_by_name (G_OBJECT (oss), - "device-removed", - mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); + for (i = 0; i < OSS_MAX_DEVICES; i++) { + gchar *path = g_strdup_printf ("/dev/mixer%i", i); - g_object_unref (device); + device = g_hash_table_lookup (oss->priv->devices, path); + if (device == NULL && i == 0) + device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer"); + + if (device != NULL) { + stream = oss_device_get_output_stream (device); + if (stream != NULL) { + _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + g_free (path); + return; + } + } + g_free (path); + } + + /* In the worst case unset the default stream */ + _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), NULL); } diff --git a/backends/oss/oss-backend.h b/backends/oss/oss-backend.h index b766799..325b61c 100644 --- a/backends/oss/oss-backend.h +++ b/backends/oss/oss-backend.h @@ -21,6 +21,7 @@ #include #include +#include #include #define OSS_TYPE_BACKEND \ @@ -42,7 +43,7 @@ typedef struct _OssBackendPrivate OssBackendPrivate; struct _OssBackend { - GObject parent; + MateMixerBackend parent; /*< private >*/ OssBackendPrivate *priv; @@ -50,7 +51,7 @@ struct _OssBackend struct _OssBackendClass { - GObjectClass parent_class; + MateMixerBackendClass parent_class; }; GType oss_backend_get_type (void) G_GNUC_CONST; diff --git a/backends/oss/oss-common.h b/backends/oss/oss-common.h index 7251b86..28a138d 100644 --- a/backends/oss/oss-common.h +++ b/backends/oss/oss-common.h @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef HAVE_SYS_SOUNDCARD_H # include 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; } diff --git a/backends/oss/oss-device.h b/backends/oss/oss-device.h index fe40eb2..261a884 100644 --- a/backends/oss/oss-device.h +++ b/backends/oss/oss-device.h @@ -20,6 +20,9 @@ #include #include +#include + +#include "oss-stream.h" G_BEGIN_DECLS @@ -42,7 +45,7 @@ typedef struct _OssDevicePrivate OssDevicePrivate; struct _OssDevice { - GObject parent; + MateMixerDevice parent; /*< private >*/ OssDevicePrivate *priv; @@ -50,23 +53,24 @@ struct _OssDevice struct _OssDeviceClass { - GObjectClass parent; + MateMixerDeviceClass parent; }; -GType oss_device_get_type (void) G_GNUC_CONST; - -OssDevice * oss_device_new (const gchar *path, - gint fd); +GType oss_device_get_type (void) G_GNUC_CONST; -gboolean oss_device_read (OssDevice *device); +OssDevice * oss_device_new (const gchar *name, + const gchar *label, + const gchar *path, + gint fd); -const gchar * oss_device_get_path (OssDevice *odevice); +gboolean oss_device_open (OssDevice *device); +gboolean oss_device_load (OssDevice *device); -MateMixerStream *oss_device_get_input_stream (OssDevice *odevice); -MateMixerStream *oss_device_get_output_stream (OssDevice *odevice); +gint oss_device_get_fd (OssDevice *device); +const gchar *oss_device_get_path (OssDevice *device); -gboolean oss_device_set_description (OssDevice *odevice, - const gchar *description); +OssStream * oss_device_get_input_stream (OssDevice *device); +OssStream * oss_device_get_output_stream (OssDevice *device); G_END_DECLS diff --git a/backends/oss/oss-stream-control.c b/backends/oss/oss-stream-control.c index 0b1db26..5161528 100644 --- a/backends/oss/oss-stream-control.c +++ b/backends/oss/oss-stream-control.c @@ -15,14 +15,13 @@ * License along with this library; if not, see . */ -#include #include #include #include #include -#include -#include +#include +#include #include "oss-common.h" #include "oss-stream-control.h" @@ -30,437 +29,279 @@ struct _OssStreamControlPrivate { gint fd; - gint dev_number; - gchar *name; - gchar *description; + gint devnum; guint volume[2]; gfloat balance; gboolean stereo; - MateMixerPort *port; MateMixerStreamControlRole role; MateMixerStreamControlFlags flags; }; -enum { - PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_FLAGS, - PROP_MUTE, - PROP_VOLUME, - PROP_BALANCE, - PROP_FADE, - PROP_FD, - PROP_DEV_NUMBER, - PROP_STEREO -}; - -static void mate_mixer_stream_control_interface_init (MateMixerStreamControlInterface *iface); - -static void oss_stream_control_class_init (OssStreamControlClass *klass); +static void oss_stream_control_class_init (OssStreamControlClass *klass); -static void oss_stream_control_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void oss_stream_control_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); +static void oss_stream_control_init (OssStreamControl *control); +static void oss_stream_control_finalize (GObject *object); -static void oss_stream_control_init (OssStreamControl *octl); -static void oss_stream_control_finalize (GObject *object); +G_DEFINE_TYPE (OssStreamControl, oss_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL) -G_DEFINE_TYPE_WITH_CODE (OssStreamControl, oss_stream_control, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM_CONTROL, - mate_mixer_stream_control_interface_init)) +static gboolean oss_stream_control_set_mute (MateMixerStreamControl *mmsc, + gboolean mute); -static const gchar * oss_stream_control_get_name (MateMixerStreamControl *ctl); -static const gchar * oss_stream_control_get_description (MateMixerStreamControl *ctl); +static guint oss_stream_control_get_num_channels (MateMixerStreamControl *mmsc); -static guint oss_stream_control_get_num_channels (MateMixerStreamControl *ctl); +static guint oss_stream_control_get_volume (MateMixerStreamControl *mmsc); -static gboolean oss_stream_control_set_volume (MateMixerStreamControl *ctl, +static gboolean oss_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume); -static guint oss_stream_control_get_channel_volume (MateMixerStreamControl *ctl, +static gboolean oss_stream_control_has_channel_position (MateMixerStreamControl *mmsc, + MateMixerChannelPosition position); +static MateMixerChannelPosition oss_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel); -static gboolean oss_stream_control_set_channel_volume (MateMixerStreamControl *ctl, - guint channel, - guint volume); -static MateMixerChannelPosition oss_stream_control_get_channel_position (MateMixerStreamControl *ctl, +static guint oss_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel); -static gboolean oss_stream_control_has_channel_position (MateMixerStreamControl *ctl, - MateMixerChannelPosition position); +static gboolean oss_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, + guint channel, + guint volume); -static gboolean oss_stream_control_set_balance (MateMixerStreamControl *ctl, +static gboolean oss_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance); -static guint oss_stream_control_get_min_volume (MateMixerStreamControl *ctl); -static guint oss_stream_control_get_max_volume (MateMixerStreamControl *ctl); -static guint oss_stream_control_get_normal_volume (MateMixerStreamControl *ctl); -static guint oss_stream_control_get_base_volume (MateMixerStreamControl *ctl); +static guint oss_stream_control_get_min_volume (MateMixerStreamControl *mmsc); +static guint oss_stream_control_get_max_volume (MateMixerStreamControl *mmsc); +static guint oss_stream_control_get_normal_volume (MateMixerStreamControl *mmsc); +static guint oss_stream_control_get_base_volume (MateMixerStreamControl *mmsc); -static void -mate_mixer_stream_control_interface_init (MateMixerStreamControlInterface *iface) -{ - iface->get_name = oss_stream_control_get_name; - iface->get_description = oss_stream_control_get_description; - iface->get_num_channels = oss_stream_control_get_num_channels; - iface->set_volume = oss_stream_control_set_volume; - iface->get_channel_volume = oss_stream_control_get_channel_volume; - iface->set_channel_volume = oss_stream_control_set_channel_volume; - iface->get_channel_position = oss_stream_control_get_channel_position; - iface->has_channel_position = oss_stream_control_has_channel_position; - iface->set_balance = oss_stream_control_set_balance; - iface->get_min_volume = oss_stream_control_get_min_volume; - iface->get_max_volume = oss_stream_control_get_max_volume; - iface->get_normal_volume = oss_stream_control_get_normal_volume; - iface->get_base_volume = oss_stream_control_get_base_volume; -} +static gboolean write_volume (OssStreamControl *control, + gint volume); static void oss_stream_control_class_init (OssStreamControlClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerStreamControlClass *control_class; object_class = G_OBJECT_CLASS (klass); - object_class->finalize = oss_stream_control_finalize; - object_class->get_property = oss_stream_control_get_property; - object_class->set_property = oss_stream_control_set_property; - - 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_install_property (object_class, - PROP_DEV_NUMBER, - g_param_spec_int ("dev-number", - "Dev number", - "OSS device number", - G_MININT, - G_MAXINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_STEREO, - g_param_spec_boolean ("stereo", - "Stereo", - "Stereo", - FALSE, - 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_FLAGS, "flags"); - g_object_class_override_property (object_class, PROP_MUTE, "mute"); - g_object_class_override_property (object_class, PROP_VOLUME, "volume"); - g_object_class_override_property (object_class, PROP_BALANCE, "balance"); - g_object_class_override_property (object_class, PROP_FADE, "fade"); + object_class->finalize = oss_stream_control_finalize; + + control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = oss_stream_control_set_mute; + control_class->get_num_channels = oss_stream_control_get_num_channels; + control_class->get_volume = oss_stream_control_get_volume; + control_class->set_volume = oss_stream_control_set_volume; + control_class->get_channel_volume = oss_stream_control_get_channel_volume; + control_class->set_channel_volume = oss_stream_control_set_channel_volume; + control_class->get_channel_position = oss_stream_control_get_channel_position; + control_class->has_channel_position = oss_stream_control_has_channel_position; + control_class->set_balance = oss_stream_control_set_balance; + control_class->get_min_volume = oss_stream_control_get_min_volume; + control_class->get_max_volume = oss_stream_control_get_max_volume; + control_class->get_normal_volume = oss_stream_control_get_normal_volume; + control_class->get_base_volume = oss_stream_control_get_base_volume; g_type_class_add_private (object_class, sizeof (OssStreamControlPrivate)); } static void -oss_stream_control_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - OssStreamControl *octl; - - octl = OSS_STREAM_CONTROL (object); - - switch (param_id) { - case PROP_NAME: - g_value_set_string (value, octl->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, octl->priv->description); - break; - case PROP_FLAGS: - g_value_set_flags (value, octl->priv->flags); - break; - case PROP_MUTE: - /* Not supported */ - g_value_set_boolean (value, FALSE); - break; - case PROP_VOLUME: - g_value_set_uint (value, MAX (octl->priv->volume[0], octl->priv->volume[1])); - break; - case PROP_BALANCE: - g_value_set_float (value, octl->priv->balance); - break; - case PROP_FADE: - /* Not supported */ - g_value_set_float (value, 0.0f); - break; - case PROP_FD: - g_value_set_int (value, octl->priv->fd); - break; - case PROP_DEV_NUMBER: - g_value_set_int (value, octl->priv->dev_number); - break; - case PROP_STEREO: - g_value_set_boolean (value, octl->priv->stereo); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -oss_stream_control_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) +oss_stream_control_init (OssStreamControl *control) { - OssStreamControl *octl; - - octl = OSS_STREAM_CONTROL (object); - - switch (param_id) { - case PROP_FD: - octl->priv->fd = dup (g_value_get_int (value)); - break; - case PROP_DEV_NUMBER: - octl->priv->dev_number = g_value_get_int (value); - break; - case PROP_STEREO: - octl->priv->stereo = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -oss_stream_control_init (OssStreamControl *octl) -{ - octl->priv = G_TYPE_INSTANCE_GET_PRIVATE (octl, - OSS_TYPE_STREAM_CONTROL, - OssStreamControlPrivate); + control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, + OSS_TYPE_STREAM_CONTROL, + OssStreamControlPrivate); } static void oss_stream_control_finalize (GObject *object) { - OssStreamControl *octl; - - octl = OSS_STREAM_CONTROL (object); + OssStreamControl *control; - g_free (octl->priv->name); - g_free (octl->priv->description); + control = OSS_STREAM_CONTROL (object); - if (octl->priv->fd != -1) - g_close (octl->priv->fd, NULL); + close (control->priv->fd); G_OBJECT_CLASS (oss_stream_control_parent_class)->finalize (object); } OssStreamControl * -oss_stream_control_new (gint fd, - gint dev_number, - const gchar *name, - const gchar *description, - gboolean stereo) +oss_stream_control_new (const gchar *name, + const gchar *label, + MateMixerStreamControlRole role, + gint fd, + gint devnum, + gboolean stereo) { - OssStreamControl *ctl; - - ctl = g_object_new (OSS_TYPE_STREAM_CONTROL, - "fd", fd, - "dev-number", dev_number, - "stereo", stereo, - NULL); - - ctl->priv->name = g_strdup (name); - ctl->priv->description = g_strdup (description); + OssStreamControl *control; + MateMixerStreamControlFlags flags; - return ctl; + flags = MATE_MIXER_STREAM_CONTROL_HAS_VOLUME | + MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME; + if (stereo == TRUE) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + + control = g_object_new (OSS_TYPE_STREAM_CONTROL, + "name", name, + "label", label, + "flags", flags, + NULL); + + control->priv->fd = fd; + control->priv->devnum = devnum; + control->priv->stereo = stereo; + return control; } gboolean -oss_stream_control_update (OssStreamControl *octl) +oss_stream_control_update (OssStreamControl *control) { gint v; gint ret; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); - ret = ioctl (octl->priv->fd, MIXER_READ (octl->priv->dev_number), &v); + ret = ioctl (control->priv->fd, MIXER_READ (control->priv->devnum), &v); if (ret < 0) { g_warning ("Failed to read volume: %s", g_strerror (errno)); return FALSE; } - octl->priv->volume[0] = v & 0xFF; + control->priv->volume[0] = v & 0xFF; - if (octl->priv->stereo == TRUE) - octl->priv->volume[1] = (v >> 8) & 0xFF; - - return TRUE; -} - -gboolean -oss_stream_control_set_port (OssStreamControl *octl, MateMixerPort *port) -{ - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + if (control->priv->stereo == TRUE) { + gfloat balance; + guint left; + guint right; - // XXX provide property + control->priv->volume[1] = (v >> 8) & 0xFF; - if (octl->priv->port != NULL) - g_object_unref (octl->priv->port); + /* Calculate balance */ + left = control->priv->volume[0]; + right = control->priv->volume[1]; + if (left == right) + balance = 0.0f; + else if (left > right) + balance = -1.0f + ((gfloat) right / (gfloat) left); + else + balance = +1.0f - ((gfloat) left / (gfloat) right); - octl->priv->port = g_object_ref (port); + _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), + balance); + } return TRUE; } -gboolean -oss_stream_control_set_role (OssStreamControl *octl, MateMixerStreamControlRole role) +static gboolean +oss_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute) { - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); - octl->priv->role = role; + // TODO return TRUE; } -static const gchar * -oss_stream_control_get_name (MateMixerStreamControl *ctl) +static guint +oss_stream_control_get_num_channels (MateMixerStreamControl *mmsc) { - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), NULL); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), 0); - return OSS_STREAM_CONTROL (ctl)->priv->name; + return (OSS_STREAM_CONTROL (mmsc)->priv->stereo == TRUE) ? 2 : 1; } -static const gchar * -oss_stream_control_get_description (MateMixerStreamControl *ctl) +static guint +oss_stream_control_get_volume (MateMixerStreamControl *mmsc) { - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), NULL); + OssStreamControl *control; - return OSS_STREAM_CONTROL (ctl)->priv->description; -} + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), 0); -static guint -oss_stream_control_get_num_channels (MateMixerStreamControl *ctl) -{ - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), 0); + control = OSS_STREAM_CONTROL (mmsc); - if (OSS_STREAM_CONTROL (ctl)->priv->stereo == TRUE) - return 2; + if (control->priv->stereo == TRUE) + return MAX (control->priv->volume[0], control->priv->volume[1]); else - return 1; + return control->priv->volume[0]; } static gboolean -oss_stream_control_set_volume (MateMixerStreamControl *ctl, guint volume) +oss_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume) { - OssStreamControl *octl; - int v; - int ret; - - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + OssStreamControl *control; + gint v; - octl = OSS_STREAM_CONTROL (ctl); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); - /* Some backends may allow setting higher value than maximum, but not here */ - if (volume > 100) - volume = 100; + control = OSS_STREAM_CONTROL (mmsc); - v = volume; - if (octl->priv->stereo == TRUE) + v = CLAMP (volume, 0, 100); + if (control->priv->stereo == TRUE) v |= (volume & 0xFF) << 8; - ret = ioctl (octl->priv->fd, MIXER_WRITE (octl->priv->dev_number), &v); - if (ret < 0) { - g_warning ("Failed to set volume: %s", g_strerror (errno)); - return FALSE; - } - return TRUE; + return write_volume (control, v); } static guint -oss_stream_control_get_channel_volume (MateMixerStreamControl *ctl, guint channel) +oss_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel) { - OssStreamControl *octl; + OssStreamControl *control; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), 0); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), 0); - octl = OSS_STREAM_CONTROL (ctl); + control = OSS_STREAM_CONTROL (mmsc); - if (channel > 1) - return 0; - - /* Right channel on mono stream will always have zero volume */ - return octl->priv->volume[channel]; + if (control->priv->stereo == TRUE) { + if (channel == 0 || channel == 1) + return control->priv->volume[channel]; + } else { + if (channel == 0) + return control->priv->volume[0]; + } + return 0; } static gboolean -oss_stream_control_set_channel_volume (MateMixerStreamControl *ctl, +oss_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume) { - OssStreamControl *octl; - int ret; - int v; + OssStreamControl *control; + gint v; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); - if (channel > 1) - return FALSE; - - octl = OSS_STREAM_CONTROL (ctl); - - /* Some backends may allow setting higher value than maximum, but not here */ - if (volume > 100) - volume = 100; - - if (channel == 0) - v = volume; - else - v = octl->priv->volume[0]; + control = OSS_STREAM_CONTROL (mmsc); + volume = CLAMP (volume, 0, 100); - if (channel == 1) { - if (octl->priv->stereo == FALSE) + if (control->priv->stereo == TRUE) { + if (channel > 1) return FALSE; - v |= volume << 8; - } else - v |= octl->priv->volume[1] << 8; + /* Stereo channel volume - left channel is in the lowest 8 bits and + * right channel is in the higher 8 bits */ + if (channel == 0) + v = volume | (control->priv->volume[1] << 8); + else + v = control->priv->volume[0] | (volume << 8); + } else { + if (channel > 0) + return FALSE; - ret = ioctl (octl->priv->fd, MIXER_WRITE (octl->priv->dev_number), &v); - if (ret < 0) { - g_warning ("Failed to set channel volume: %s", g_strerror (errno)); - return FALSE; + /* Single channel volume - only lowest 8 bits are used */ + v = volume; } - return TRUE; + + return write_volume (control, v); } static MateMixerChannelPosition -oss_stream_control_get_channel_position (MateMixerStreamControl *ctl, guint channel) +oss_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel) { - OssStreamControl *octl; + OssStreamControl *control; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), MATE_MIXER_CHANNEL_UNKNOWN); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN); - octl = OSS_STREAM_CONTROL (ctl); + control = OSS_STREAM_CONTROL (mmsc); - if (octl->priv->stereo == TRUE) { + if (control->priv->stereo == TRUE) { if (channel == 0) return MATE_MIXER_CHANNEL_FRONT_LEFT; else if (channel == 1) @@ -473,57 +314,79 @@ oss_stream_control_get_channel_position (MateMixerStreamControl *ctl, guint chan } static gboolean -oss_stream_control_has_channel_position (MateMixerStreamControl *ctl, +oss_stream_control_has_channel_position (MateMixerStreamControl *mmsc, MateMixerChannelPosition position) { - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); if (position == MATE_MIXER_CHANNEL_MONO) - return OSS_STREAM_CONTROL (ctl)->priv->stereo == FALSE; + return OSS_STREAM_CONTROL (mmsc)->priv->stereo == FALSE; if (position == MATE_MIXER_CHANNEL_FRONT_LEFT || position == MATE_MIXER_CHANNEL_FRONT_RIGHT) - return OSS_STREAM_CONTROL (ctl)->priv->stereo == TRUE; + return OSS_STREAM_CONTROL (mmsc)->priv->stereo == TRUE; return FALSE; } static gboolean -oss_stream_control_set_balance (MateMixerStreamControl *ctl, gfloat balance) +oss_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance) { - OssStreamControl *octl; + OssStreamControl *control; + guint max; + gint v; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); - octl = OSS_STREAM_CONTROL (ctl); + control = OSS_STREAM_CONTROL (mmsc); - if (octl->priv->stereo == FALSE) - return FALSE; + max = MAX (control->priv->volume[0], control->priv->volume[1]); + if (balance <= 0) { + control->priv->volume[1] = (balance + 1.0f) * max; + control->priv->volume[0] = max; + } else { + control->priv->volume[0] = (1.0f - balance) * max; + control->priv->volume[1] = max; + } - // XXX - return TRUE; + v = control->priv->volume[0] | (control->priv->volume[1] << 8); + + return write_volume (control, v); } static guint -oss_stream_control_get_min_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_min_volume (MateMixerStreamControl *mmsc) { return 0; } static guint -oss_stream_control_get_max_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_max_volume (MateMixerStreamControl *mmsc) { return 100; } static guint -oss_stream_control_get_normal_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_normal_volume (MateMixerStreamControl *mmsc) { return 100; } static guint -oss_stream_control_get_base_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_base_volume (MateMixerStreamControl *mmsc) { return 100; } + +static gboolean +write_volume (OssStreamControl *control, gint volume) +{ + gint ret; + + ret = ioctl (control->priv->fd, MIXER_WRITE (control->priv->devnum), &volume); + if (ret < 0) { + g_warning ("Failed to set volume: %s", g_strerror (errno)); + return FALSE; + } + return TRUE; +} diff --git a/backends/oss/oss-stream-control.h b/backends/oss/oss-stream-control.h index 420af48..c839faf 100644 --- a/backends/oss/oss-stream-control.h +++ b/backends/oss/oss-stream-control.h @@ -20,6 +20,7 @@ #include #include +#include G_BEGIN_DECLS @@ -42,7 +43,7 @@ typedef struct _OssStreamControlPrivate OssStreamControlPrivate; struct _OssStreamControl { - GObject parent; + MateMixerStreamControl parent; /*< private >*/ OssStreamControlPrivate *priv; @@ -50,24 +51,19 @@ struct _OssStreamControl struct _OssStreamControlClass { - GObjectClass parent; + MateMixerStreamControlClass parent; }; -GType oss_stream_control_get_type (void) G_GNUC_CONST; +GType oss_stream_control_get_type (void) G_GNUC_CONST; -OssStreamControl *oss_stream_control_new (gint fd, - gint dev_number, - const gchar *name, - const gchar *description, - gboolean stereo); +OssStreamControl *oss_stream_control_new (const gchar *name, + const gchar *label, + MateMixerStreamControlRole role, + gint fd, + gint devnum, + gboolean stereo); -gboolean oss_stream_control_update (OssStreamControl *octl); - -gboolean oss_stream_control_set_port (OssStreamControl *octl, - MateMixerPort *port); - -gboolean oss_stream_control_set_role (OssStreamControl *octl, - MateMixerStreamControlRole role); +gboolean oss_stream_control_update (OssStreamControl *control); G_END_DECLS diff --git a/backends/oss/oss-stream.c b/backends/oss/oss-stream.c index 69bfd06..5f0c629 100644 --- a/backends/oss/oss-stream.c +++ b/backends/oss/oss-stream.c @@ -15,301 +15,169 @@ * License along with this library; if not, see . */ -#include #include #include +#include -#include -#include -#include - +#include "oss-device.h" #include "oss-stream.h" #include "oss-stream-control.h" struct _OssStreamPrivate { - gchar *name; - gchar *description; - MateMixerDevice *device; - MateMixerStreamFlags flags; - MateMixerStreamState state; - GHashTable *ports; - GList *ports_list; - GHashTable *controls; - GList *controls_list; - OssStreamControl *control; -}; - -enum { - PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_DEVICE, - PROP_FLAGS, - PROP_STATE, - PROP_ACTIVE_PORT, + GHashTable *controls; + OssStreamControl *control; }; -static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); - static void oss_stream_class_init (OssStreamClass *klass); -static void oss_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); - static void oss_stream_init (OssStream *ostream); static void oss_stream_dispose (GObject *object); static void oss_stream_finalize (GObject *object); -G_DEFINE_TYPE_WITH_CODE (OssStream, oss_stream, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM, - mate_mixer_stream_interface_init)) - -static const gchar * oss_stream_get_name (MateMixerStream *stream); -static const gchar * oss_stream_get_description (MateMixerStream *stream); +G_DEFINE_TYPE (OssStream, oss_stream, MATE_MIXER_TYPE_STREAM) static MateMixerStreamControl *oss_stream_get_control (MateMixerStream *stream, const gchar *name); static MateMixerStreamControl *oss_stream_get_default_control (MateMixerStream *stream); -static const GList * oss_stream_list_controls (MateMixerStream *stream); -static const GList * oss_stream_list_ports (MateMixerStream *stream); - -static void -mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) -{ - iface->get_name = oss_stream_get_name; - iface->get_description = oss_stream_get_description; - iface->get_control = oss_stream_get_control; - iface->get_default_control = oss_stream_get_default_control; - iface->list_controls = oss_stream_list_controls; - iface->list_ports = oss_stream_list_ports; -} +static GList * oss_stream_list_controls (MateMixerStream *stream); static void oss_stream_class_init (OssStreamClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerStreamClass *stream_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = oss_stream_dispose; - object_class->finalize = oss_stream_finalize; - object_class->get_property = oss_stream_get_property; + object_class->dispose = oss_stream_dispose; + object_class->finalize = oss_stream_finalize; - 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_DEVICE, "device"); - g_object_class_override_property (object_class, PROP_FLAGS, "flags"); - g_object_class_override_property (object_class, PROP_STATE, "state"); - g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port"); + stream_class = MATE_MIXER_STREAM_CLASS (klass); + stream_class->get_control = oss_stream_get_control; + stream_class->get_default_control = oss_stream_get_default_control; + stream_class->list_controls = oss_stream_list_controls; g_type_class_add_private (object_class, sizeof (OssStreamPrivate)); } static void -oss_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) +oss_stream_init (OssStream *stream) { - OssStream *ostream; - - ostream = OSS_STREAM (object); - - switch (param_id) { - case PROP_NAME: - g_value_set_string (value, ostream->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, ostream->priv->description); - break; - case PROP_DEVICE: - g_value_set_object (value, ostream->priv->device); - break; - case PROP_FLAGS: - g_value_set_flags (value, ostream->priv->flags); - break; - case PROP_STATE: - /* Not supported */ - g_value_set_enum (value, MATE_MIXER_STREAM_STATE_UNKNOWN); - break; - case PROP_ACTIVE_PORT: - // XXX - g_value_set_object (value, NULL); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} + stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, + OSS_TYPE_STREAM, + OssStreamPrivate); -static void -oss_stream_init (OssStream *ostream) -{ - ostream->priv = G_TYPE_INSTANCE_GET_PRIVATE (ostream, - OSS_TYPE_STREAM, - OssStreamPrivate); - - ostream->priv->controls = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); - - ostream->priv->ports = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); + stream->priv->controls = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); } static void oss_stream_dispose (GObject *object) { - OssStream *ostream; + OssStream *stream; - ostream = OSS_STREAM (object); + stream = OSS_STREAM (object); - g_hash_table_remove_all (ostream->priv->controls); - g_hash_table_remove_all (ostream->priv->ports); + g_clear_object (&stream->priv->control); + g_hash_table_remove_all (stream->priv->controls); - G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object); + G_OBJECT_CLASS (oss_stream_parent_class)->dispose (object); } static void oss_stream_finalize (GObject *object) { - OssStream *ostream; - - ostream = OSS_STREAM (object); + OssStream *stream; - g_free (ostream->priv->name); - g_free (ostream->priv->description); + stream = OSS_STREAM (object); - g_hash_table_destroy (ostream->priv->controls); - g_hash_table_destroy (ostream->priv->ports); + g_hash_table_destroy (stream->priv->controls); G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object); } OssStream * oss_stream_new (const gchar *name, - const gchar *description, + MateMixerDevice *device, MateMixerStreamFlags flags) { OssStream *stream; - stream = g_object_new (OSS_TYPE_STREAM, NULL); - - stream->priv->name = g_strdup (name); - stream->priv->description = g_strdup (description); - stream->priv->flags = flags; - + stream = g_object_new (OSS_TYPE_STREAM, + "name", name, + "device", device, + "flags", flags, + NULL); return stream; } gboolean -oss_stream_add_control (OssStream *ostream, OssStreamControl *octl) +oss_stream_add_control (OssStream *stream, OssStreamControl *control) { const gchar *name; - g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); - name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (octl)); + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control)); - g_hash_table_insert (ostream->priv->controls, + g_hash_table_insert (stream->priv->controls, g_strdup (name), - octl); + control); return TRUE; } gboolean -oss_stream_set_default_control (OssStream *ostream, OssStreamControl *octl) +oss_stream_set_default_control (OssStream *stream, OssStreamControl *control) { - g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); /* This function is only used internally so avoid validating that the control * belongs to this stream */ - if (ostream->priv->control != NULL) - g_object_unref (ostream->priv->control); - - ostream->priv->control = g_object_ref (octl); - return TRUE; -} + if (stream->priv->control != NULL) + g_object_unref (stream->priv->control); -gboolean -oss_stream_add_port (OssStream *ostream, MateMixerPort *port) -{ - g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + if G_LIKELY (control != NULL) + stream->priv->control = g_object_ref (control); + else + stream->priv->control = NULL; - g_hash_table_insert (ostream->priv->ports, - g_strdup (mate_mixer_port_get_name (port)), - port); return TRUE; } -static const gchar * -oss_stream_get_name (MateMixerStream *stream) -{ - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - - return OSS_STREAM (stream)->priv->name; -} - -static const gchar * -oss_stream_get_description (MateMixerStream *stream) -{ - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - - return OSS_STREAM (stream)->priv->description; -} - static MateMixerStreamControl * -oss_stream_get_control (MateMixerStream *stream, const gchar *name) +oss_stream_get_control (MateMixerStream *mms, const gchar *name) { - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); + g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); g_return_val_if_fail (name != NULL, NULL); - return g_hash_table_lookup (OSS_STREAM (stream)->priv->controls, name); + return g_hash_table_lookup (OSS_STREAM (mms)->priv->controls, name); } static MateMixerStreamControl * -oss_stream_get_default_control (MateMixerStream *stream) -{ - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - - return MATE_MIXER_STREAM_CONTROL (OSS_STREAM (stream)->priv->control); -} - -static const GList * -oss_stream_list_controls (MateMixerStream *stream) +oss_stream_get_default_control (MateMixerStream *mms) { - OssStream *ostream; - - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - - ostream = OSS_STREAM (stream); + g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); - if (ostream->priv->controls_list == NULL) - ostream->priv->controls_list = g_hash_table_get_values (ostream->priv->controls); - - return ostream->priv->controls_list; + return MATE_MIXER_STREAM_CONTROL (OSS_STREAM (mms)->priv->control); } -static const GList * -oss_stream_list_ports (MateMixerStream *stream) +static GList * +oss_stream_list_controls (MateMixerStream *mms) { - OssStream *ostream; - - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); + GList *list; - ostream = OSS_STREAM (stream); + g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); - if (ostream->priv->ports_list == NULL) - ostream->priv->ports_list = g_hash_table_get_values (ostream->priv->ports); + /* Convert the hash table to a linked list, this list is expected to be + * cached in the main library */ + list = g_hash_table_get_values (OSS_STREAM (mms)->priv->controls); + if (list != NULL) + g_list_foreach (list, (GFunc) g_object_ref, NULL); - return ostream->priv->ports_list; + return list; } diff --git a/backends/oss/oss-stream.h b/backends/oss/oss-stream.h index d6c2fb2..0470eb7 100644 --- a/backends/oss/oss-stream.h +++ b/backends/oss/oss-stream.h @@ -20,7 +20,9 @@ #include #include +#include +#include "oss-device.h" #include "oss-stream-control.h" G_BEGIN_DECLS @@ -44,7 +46,7 @@ typedef struct _OssStreamPrivate OssStreamPrivate; struct _OssStream { - GObject parent; + MateMixerStream parent; /*< private >*/ OssStreamPrivate *priv; @@ -52,23 +54,20 @@ struct _OssStream struct _OssStreamClass { - GObjectClass parent; + MateMixerStreamClass parent; }; -GType oss_stream_get_type (void) G_GNUC_CONST; +GType oss_stream_get_type (void) G_GNUC_CONST; -OssStream * oss_stream_new (const gchar *name, - const gchar *description, - MateMixerStreamFlags flags); +OssStream *oss_stream_new (const gchar *name, + MateMixerDevice *device, + MateMixerStreamFlags flags); -gboolean oss_stream_add_control (OssStream *stream, - OssStreamControl *ctl); +gboolean oss_stream_add_control (OssStream *stream, + OssStreamControl *control); -gboolean oss_stream_set_default_control (OssStream *stream, - OssStreamControl *ctl); - -gboolean oss_stream_add_port (OssStream *ostream, - MateMixerPort *port); +gboolean oss_stream_set_default_control (OssStream *stream, + OssStreamControl *control); G_END_DECLS -- cgit v1.2.1