summaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
authorMichal Ratajsky <[email protected]>2014-08-12 04:56:55 +0200
committerMichal Ratajsky <[email protected]>2014-08-12 04:56:55 +0200
commit6c6d4239ddc807e922df3874654f99eea291aadb (patch)
tree558f48fc965476344a946d93088680db3b5bac41 /backends
parent8436ec1641eee8868128755f6d1475230cea25e6 (diff)
downloadlibmatemixer-6c6d4239ddc807e922df3874654f99eea291aadb.tar.bz2
libmatemixer-6c6d4239ddc807e922df3874654f99eea291aadb.tar.xz
Add ALSA, improve OSS and remove OSS4
Diffstat (limited to 'backends')
-rw-r--r--backends/Makefile.am8
-rw-r--r--backends/alsa/Makefile.am47
-rw-r--r--backends/alsa/alsa-backend.c508
-rw-r--r--backends/alsa/alsa-backend.h62
-rw-r--r--backends/alsa/alsa-constants.c72
-rw-r--r--backends/alsa/alsa-constants.h (renamed from backends/oss4/oss4-common.h)31
-rw-r--r--backends/alsa/alsa-device.c941
-rw-r--r--backends/alsa/alsa-device.h74
-rw-r--r--backends/alsa/alsa-element.c53
-rw-r--r--backends/alsa/alsa-element.h61
-rw-r--r--backends/alsa/alsa-stream-control.c739
-rw-r--r--backends/alsa/alsa-stream-control.h111
-rw-r--r--backends/alsa/alsa-stream-input-control.c329
-rw-r--r--backends/alsa/alsa-stream-input-control.h64
-rw-r--r--backends/alsa/alsa-stream-output-control.c329
-rw-r--r--backends/alsa/alsa-stream-output-control.h64
-rw-r--r--backends/alsa/alsa-stream.c273
-rw-r--r--backends/alsa/alsa-stream.h88
-rw-r--r--backends/alsa/alsa-switch-option.c74
-rw-r--r--backends/alsa/alsa-switch-option.h68
-rw-r--r--backends/alsa/alsa-switch.c227
-rw-r--r--backends/alsa/alsa-switch.h65
-rw-r--r--backends/alsa/alsa-toggle.c219
-rw-r--r--backends/alsa/alsa-toggle.h74
-rw-r--r--backends/oss/oss-backend.c699
-rw-r--r--backends/oss/oss-backend.h5
-rw-r--r--backends/oss/oss-common.h1
-rw-r--r--backends/oss/oss-device.c609
-rw-r--r--backends/oss/oss-device.h28
-rw-r--r--backends/oss/oss-stream-control.c527
-rw-r--r--backends/oss/oss-stream-control.h26
-rw-r--r--backends/oss/oss-stream.c266
-rw-r--r--backends/oss/oss-stream.h25
-rw-r--r--backends/oss4/Makefile.am29
-rw-r--r--backends/oss4/oss4-backend.c487
-rw-r--r--backends/oss4/oss4-backend.h62
-rw-r--r--backends/oss4/oss4-device.c328
-rw-r--r--backends/oss4/oss4-device.h72
38 files changed, 5450 insertions, 2295 deletions
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 0cf8dd4..4a8afff 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -8,12 +8,12 @@ if HAVE_PULSEAUDIO
SUBDIRS += pulse
endif
-if HAVE_OSS
-SUBDIRS += oss
+if HAVE_ALSA
+SUBDIRS += alsa
endif
-if HAVE_OSS4
-SUBDIRS += oss4
+if HAVE_OSS
+SUBDIRS += oss
endif
-include $(top_srcdir)/git.mk
diff --git a/backends/alsa/Makefile.am b/backends/alsa/Makefile.am
new file mode 100644
index 0000000..220bb3b
--- /dev/null
+++ b/backends/alsa/Makefile.am
@@ -0,0 +1,47 @@
+backenddir = $(libdir)/libmatemixer
+
+backend_LTLIBRARIES = libmatemixer-alsa.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -DG_LOG_DOMAIN=\"libmatemixer-alsa\"
+
+libmatemixer_alsa_la_CFLAGS = \
+ $(GLIB_CFLAGS) \
+ $(ALSA_CFLAGS)
+
+libmatemixer_alsa_la_SOURCES = \
+ alsa-backend.c \
+ alsa-backend.h \
+ alsa-constants.c \
+ alsa-constants.h \
+ alsa-device.c \
+ alsa-device.h \
+ alsa-element.c \
+ alsa-element.h \
+ alsa-stream.c \
+ alsa-stream.h \
+ alsa-stream-control.c \
+ alsa-stream-control.h \
+ alsa-stream-input-control.c \
+ alsa-stream-input-control.h \
+ alsa-stream-output-control.c \
+ alsa-stream-output-control.h \
+ alsa-switch.c \
+ alsa-switch.h \
+ alsa-switch-option.c \
+ alsa-switch-option.h \
+ alsa-toggle.c \
+ alsa-toggle.h
+
+libmatemixer_alsa_la_LIBADD = \
+ $(GLIB_LIBS) \
+ $(ALSA_LIBS)
+
+libmatemixer_alsa_la_LDFLAGS = \
+ -avoid-version \
+ -no-undefined \
+ -export-dynamic \
+ -module
+
+-include $(top_srcdir)/git.mk
diff --git a/backends/alsa/alsa-backend.c b/backends/alsa/alsa-backend.c
new file mode 100644
index 0000000..7a17b85
--- /dev/null
+++ b/backends/alsa/alsa-backend.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "alsa-backend.h"
+#include "alsa-device.h"
+#include "alsa-stream.h"
+
+#define BACKEND_NAME "ALSA"
+#define BACKEND_PRIORITY 9
+
+struct _AlsaBackendPrivate
+{
+ GSource *timeout_source;
+ GHashTable *devices;
+ GHashTable *devices_ids;
+};
+
+static void alsa_backend_class_init (AlsaBackendClass *klass);
+static void alsa_backend_class_finalize (AlsaBackendClass *klass);
+static void alsa_backend_init (AlsaBackend *alsa);
+static void alsa_backend_dispose (GObject *object);
+static void alsa_backend_finalize (GObject *object);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+G_DEFINE_DYNAMIC_TYPE (AlsaBackend, alsa_backend, MATE_MIXER_TYPE_BACKEND)
+#pragma clang diagnostic pop
+
+static gboolean alsa_backend_open (MateMixerBackend *backend);
+static void alsa_backend_close (MateMixerBackend *backend);
+static GList * alsa_backend_list_devices (MateMixerBackend *backend);
+static GList * alsa_backend_list_streams (MateMixerBackend *backend);
+
+static gboolean read_devices (AlsaBackend *alsa);
+
+static gboolean read_device (AlsaBackend *alsa,
+ const gchar *card);
+
+static void add_device (AlsaBackend *alsa,
+ AlsaDevice *device);
+
+static void remove_device (AlsaBackend *alsa,
+ AlsaDevice *device);
+static void remove_stream (AlsaBackend *alsa,
+ const gchar *name);
+
+static void select_default_input_stream (AlsaBackend *alsa);
+static void select_default_output_stream (AlsaBackend *alsa);
+
+static MateMixerBackendInfo info;
+
+void
+backend_module_init (GTypeModule *module)
+{
+ alsa_backend_register_type (module);
+
+ info.name = BACKEND_NAME;
+ info.priority = BACKEND_PRIORITY;
+ info.g_type = ALSA_TYPE_BACKEND;
+ info.backend_type = MATE_MIXER_BACKEND_ALSA;
+}
+
+const MateMixerBackendInfo *backend_module_get_info (void)
+{
+ return &info;
+}
+
+static void
+alsa_backend_class_init (AlsaBackendClass *klass)
+{
+ GObjectClass *object_class;
+ MateMixerBackendClass *backend_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = alsa_backend_dispose;
+ object_class->finalize = alsa_backend_finalize;
+
+ backend_class = MATE_MIXER_BACKEND_CLASS (klass);
+ backend_class->open = alsa_backend_open;
+ backend_class->close = alsa_backend_close;
+ backend_class->list_devices = alsa_backend_list_devices;
+ backend_class->list_streams = alsa_backend_list_streams;
+
+ g_type_class_add_private (object_class, sizeof (AlsaBackendPrivate));
+}
+
+/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE() */
+static void
+alsa_backend_class_finalize (AlsaBackendClass *klass)
+{
+}
+
+static void
+alsa_backend_init (AlsaBackend *alsa)
+{
+ alsa->priv = G_TYPE_INSTANCE_GET_PRIVATE (alsa,
+ ALSA_TYPE_BACKEND,
+ AlsaBackendPrivate);
+
+ alsa->priv->devices = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ alsa->priv->devices_ids = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+}
+
+static void
+alsa_backend_dispose (GObject *object)
+{
+ MateMixerBackend *backend;
+ MateMixerState state;
+
+ backend = MATE_MIXER_BACKEND (object);
+
+ state = mate_mixer_backend_get_state (backend);
+ if (state != MATE_MIXER_STATE_IDLE)
+ alsa_backend_close (backend);
+
+ G_OBJECT_CLASS (alsa_backend_parent_class)->dispose (object);
+}
+
+static void
+alsa_backend_finalize (GObject *object)
+{
+ AlsaBackend *alsa;
+
+ alsa = ALSA_BACKEND (object);
+
+ g_hash_table_unref (alsa->priv->devices);
+ g_hash_table_unref (alsa->priv->devices_ids);
+
+ G_OBJECT_CLASS (alsa_backend_parent_class)->finalize (object);
+}
+
+static gboolean
+alsa_backend_open (MateMixerBackend *backend)
+{
+ AlsaBackend *alsa;
+
+ g_return_val_if_fail (ALSA_IS_BACKEND (backend), FALSE);
+
+ alsa = ALSA_BACKEND (backend);
+
+ /* Poll ALSA for changes every 500 milliseconds, this actually only
+ * discovers added or changed sound cards, sound card related events
+ * are handled by AlsaDevices */
+ alsa->priv->timeout_source = g_timeout_source_new_seconds (1);
+ g_source_set_callback (alsa->priv->timeout_source,
+ (GSourceFunc) read_devices,
+ alsa,
+ NULL);
+ g_source_attach (alsa->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 (alsa);
+
+ _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_READY);
+ return TRUE;
+}
+
+void
+alsa_backend_close (MateMixerBackend *backend)
+{
+ AlsaBackend *alsa;
+
+ g_return_if_fail (ALSA_IS_BACKEND (backend));
+
+ alsa = ALSA_BACKEND (backend);
+
+ g_source_destroy (alsa->priv->timeout_source);
+
+ g_hash_table_remove_all (alsa->priv->devices);
+ g_hash_table_remove_all (alsa->priv->devices_ids);
+
+ _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE);
+}
+
+static GList *
+alsa_backend_list_devices (MateMixerBackend *backend)
+{
+ GList *list;
+
+ g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL);
+
+ /* 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 (ALSA_BACKEND (backend)->priv->devices);
+ if (list != NULL)
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+ return list;
+}
+
+static GList *
+alsa_backend_list_streams (MateMixerBackend *backend)
+{
+ AlsaBackend *alsa;
+ GHashTableIter iter;
+ gpointer value;
+ GList *list = NULL;
+
+ g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL);
+
+ alsa = ALSA_BACKEND (backend);
+
+ /* 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, alsa->priv->devices);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ AlsaDevice *device = ALSA_DEVICE (value);
+ AlsaStream *stream;
+
+ stream = alsa_device_get_output_stream (device);
+ if (stream != NULL)
+ list = g_list_prepend (list, stream);
+ stream = alsa_device_get_input_stream (device);
+ if (stream != NULL)
+ list = g_list_prepend (list, stream);
+ }
+
+ if (list != NULL)
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+ return list;
+}
+
+static gboolean
+read_devices (AlsaBackend *alsa)
+{
+ gint num;
+ gint ret;
+ gchar card[16];
+ gboolean changed = FALSE;
+
+ /* Read the default device first, it will be either one of the hardware cards
+ * that will be queried later, or a software mixer */
+ if (read_device (alsa, "default") == TRUE)
+ changed = TRUE;
+
+ for (num = -1;;) {
+ /* Read number of the next sound card */
+ ret = snd_card_next (&num);
+ if (ret < 0 ||
+ num < 0)
+ break;
+
+ g_snprintf (card, sizeof (card), "hw:%d", num);
+
+ if (read_device (alsa, card) == TRUE)
+ changed = TRUE;
+ }
+
+ /* If any card has been added, make sure we have the most suitable default
+ * input and output streams */
+ if (changed == TRUE) {
+ select_default_input_stream (alsa);
+ select_default_output_stream (alsa);
+ }
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+read_device (AlsaBackend *alsa, const gchar *card)
+{
+ AlsaDevice *device;
+ snd_ctl_t *ctl;
+ snd_ctl_card_info_t *info;
+ const gchar *id;
+ gint ret;
+
+ /* The device may be already known, remove it if it's known and fails
+ * to be read, this happens for example when PulseAudio is killed */
+ device = g_hash_table_lookup (alsa->priv->devices, card);
+
+ ret = snd_ctl_open (&ctl, card, 0);
+ if (ret < 0) {
+ g_warning ("Failed to open ALSA control for %s: %s",
+ card,
+ snd_strerror (ret));
+ if (device != NULL)
+ remove_device (alsa, device);
+ return FALSE;
+ }
+
+ snd_ctl_card_info_alloca (&info);
+
+ ret = snd_ctl_card_info (ctl, info);
+ if (ret < 0) {
+ g_warning ("Failed to read card info: %s", snd_strerror (ret));
+ if (device != NULL)
+ remove_device (alsa, device);
+
+ snd_ctl_close (ctl);
+ return FALSE;
+ }
+
+ id = snd_ctl_card_info_get_id (info);
+
+ /* We also keep a list of device identifiers to be sure no card is
+ * added twice, this could commonly happen because some card may
+ * also be assigned to the "default" ALSA device */
+ if (g_hash_table_contains (alsa->priv->devices_ids, id) == TRUE) {
+ snd_ctl_close (ctl);
+ return FALSE;
+ }
+
+ device = alsa_device_new (card, snd_ctl_card_info_get_name (info));
+
+ if (alsa_device_open (device) == FALSE) {
+ g_object_unref (device);
+ snd_ctl_close (ctl);
+ return FALSE;
+ }
+
+ g_object_set_data_full (G_OBJECT (device),
+ "__matemixer_alsa_device_id",
+ g_strdup (id),
+ g_free);
+
+ add_device (alsa, device);
+
+ snd_ctl_close (ctl);
+ return TRUE;
+}
+
+static void
+add_device (AlsaBackend *alsa, AlsaDevice *device)
+{
+ const gchar *name;
+
+ name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+
+ g_hash_table_insert (alsa->priv->devices,
+ g_strdup (name),
+ g_object_ref (device));
+
+ /* Remember the device identifier, use a single string copy as we only free
+ * the hash table key */
+ g_hash_table_add (alsa->priv->devices_ids,
+ g_strdup (g_object_get_data (G_OBJECT (device),
+ "__matemixer_alsa_device_id")));
+
+ g_signal_connect_swapped (G_OBJECT (device),
+ "closed",
+ G_CALLBACK (remove_device),
+ alsa);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "stream-removed",
+ G_CALLBACK (remove_stream),
+ alsa);
+
+ g_signal_emit_by_name (G_OBJECT (alsa), "device-added", name);
+
+ /* Load the device elements after emitting device-added, because the load
+ * function will most likely emit stream-added on the device and backend */
+ alsa_device_load (device);
+}
+
+static void
+remove_device (AlsaBackend *alsa, AlsaDevice *device)
+{
+ const gchar *name;
+
+ name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (device),
+ G_CALLBACK (remove_device),
+ alsa);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (device),
+ G_CALLBACK (remove_stream),
+ alsa);
+
+ /* Remove the device */
+ g_hash_table_remove (alsa->priv->devices_ids,
+ g_object_get_data (G_OBJECT (device),
+ "__matemixer_alsa_device_id"));
+
+ // XXX close the device and make it remove streams
+ g_hash_table_remove (alsa->priv->devices, name);
+ g_signal_emit_by_name (G_OBJECT (alsa),
+ "device-removed",
+ name);
+}
+
+static void
+remove_stream (AlsaBackend *alsa, const gchar *name)
+{
+ MateMixerStream *stream;
+
+ stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (alsa));
+
+ // 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 (alsa);
+
+ stream = mate_mixer_backend_get_default_output_stream (MATE_MIXER_BACKEND (alsa));
+
+ if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0)
+ select_default_output_stream (alsa);
+}
+
+static void
+select_default_input_stream (AlsaBackend *alsa)
+{
+ AlsaDevice *device;
+ AlsaStream *stream;
+ gchar card[16];
+ gint num;
+
+ /* Always prefer stream in the "default" device */
+ device = g_hash_table_lookup (alsa->priv->devices, "default");
+ if (device != NULL) {
+ stream = alsa_device_get_input_stream (device);
+ if (stream != NULL) {
+ _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa),
+ MATE_MIXER_STREAM (stream));
+ return;
+ }
+ }
+
+ /* If there is no input stream in the default device, search the cards in
+ * the correct order */
+ for (num = 0;; num++) {
+ g_snprintf (card, sizeof (card), "hw:%d", num);
+
+ device = g_hash_table_lookup (alsa->priv->devices, card);
+ if (device == NULL)
+ break;
+ stream = alsa_device_get_input_stream (device);
+ if (stream != NULL) {
+ _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa),
+ MATE_MIXER_STREAM (stream));
+ return;
+ }
+ }
+
+ /* In the worst case unset the default stream */
+ _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa), NULL);
+}
+
+static void
+select_default_output_stream (AlsaBackend *alsa)
+{
+ AlsaDevice *device;
+ AlsaStream *stream;
+ gchar card[16];
+ gint num;
+
+ /* Always prefer stream in the "default" device */
+ device = g_hash_table_lookup (alsa->priv->devices, "default");
+ if (device != NULL) {
+ stream = alsa_device_get_output_stream (device);
+ if (stream != NULL) {
+ _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa),
+ MATE_MIXER_STREAM (stream));
+ return;
+ }
+ }
+
+ /* If there is no input stream in the default device, search the cards in
+ * the correct order */
+ for (num = 0;; num++) {
+ g_snprintf (card, sizeof (card), "hw:%d", num);
+
+ device = g_hash_table_lookup (alsa->priv->devices, card);
+ if (device == NULL)
+ break;
+ stream = alsa_device_get_output_stream (device);
+ if (stream != NULL) {
+ _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa),
+ MATE_MIXER_STREAM (stream));
+ return;
+ }
+ }
+
+ /* In the worst case unset the default stream */
+ _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa), NULL);
+}
diff --git a/backends/alsa/alsa-backend.h b/backends/alsa/alsa-backend.h
new file mode 100644
index 0000000..03fedf0
--- /dev/null
+++ b/backends/alsa/alsa-backend.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_BACKEND_H
+#define ALSA_BACKEND_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#define ALSA_TYPE_BACKEND \
+ (alsa_backend_get_type ())
+#define ALSA_BACKEND(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_BACKEND, AlsaBackend))
+#define ALSA_IS_BACKEND(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_BACKEND))
+#define ALSA_BACKEND_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_BACKEND, AlsaBackendClass))
+#define ALSA_IS_BACKEND_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_BACKEND))
+#define ALSA_BACKEND_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_BACKEND, AlsaBackendClass))
+
+typedef struct _AlsaBackend AlsaBackend;
+typedef struct _AlsaBackendClass AlsaBackendClass;
+typedef struct _AlsaBackendPrivate AlsaBackendPrivate;
+
+struct _AlsaBackend
+{
+ MateMixerBackend parent;
+
+ /*< private >*/
+ AlsaBackendPrivate *priv;
+};
+
+struct _AlsaBackendClass
+{
+ MateMixerBackendClass parent_class;
+};
+
+GType alsa_backend_get_type (void) G_GNUC_CONST;
+
+/* Support function for dynamic loading of the backend module */
+void backend_module_init (GTypeModule *module);
+const MateMixerBackendInfo *backend_module_get_info (void);
+
+#endif /* ALSA_BACKEND_H */
diff --git a/backends/alsa/alsa-constants.c b/backends/alsa/alsa-constants.c
new file mode 100644
index 0000000..2124a2e
--- /dev/null
+++ b/backends/alsa/alsa-constants.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <alsa/asoundlib.h>
+#include <libmatemixer/matemixer.h>
+
+#include "alsa-constants.h"
+
+// XXX add more and probably move them somewhere else
+const AlsaControlInfo alsa_controls[] =
+{
+ { "Master", N_("Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER },
+ { "Speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER },
+ { "Capture", N_("Capture"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER },
+ { "PCM", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM },
+ { "Line", N_("Line"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT },
+ { "Mic", N_("Mic"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT },
+ { NULL }
+};
+
+const MateMixerChannelPosition alsa_channel_map_from[SND_MIXER_SCHN_LAST] =
+{
+ [SND_MIXER_SCHN_FRONT_LEFT] = MATE_MIXER_CHANNEL_FRONT_LEFT,
+ [SND_MIXER_SCHN_FRONT_RIGHT] = MATE_MIXER_CHANNEL_FRONT_RIGHT,
+ [SND_MIXER_SCHN_REAR_LEFT] = MATE_MIXER_CHANNEL_BACK_LEFT,
+ [SND_MIXER_SCHN_REAR_RIGHT] = MATE_MIXER_CHANNEL_BACK_RIGHT,
+ [SND_MIXER_SCHN_FRONT_CENTER] = MATE_MIXER_CHANNEL_FRONT_CENTER,
+ [SND_MIXER_SCHN_WOOFER] = MATE_MIXER_CHANNEL_LFE,
+ [SND_MIXER_SCHN_SIDE_LEFT] = MATE_MIXER_CHANNEL_SIDE_LEFT,
+ [SND_MIXER_SCHN_SIDE_RIGHT] = MATE_MIXER_CHANNEL_SIDE_RIGHT,
+ [SND_MIXER_SCHN_REAR_CENTER] = MATE_MIXER_CHANNEL_BACK_CENTER
+};
+
+const snd_mixer_selem_channel_id_t alsa_channel_map_to[MATE_MIXER_CHANNEL_MAX] =
+{
+ [MATE_MIXER_CHANNEL_UNKNOWN] = SND_MIXER_SCHN_UNKNOWN,
+ [MATE_MIXER_CHANNEL_MONO] = SND_MIXER_SCHN_MONO,
+ [MATE_MIXER_CHANNEL_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
+ [MATE_MIXER_CHANNEL_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
+ [MATE_MIXER_CHANNEL_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
+ [MATE_MIXER_CHANNEL_LFE] = SND_MIXER_SCHN_WOOFER,
+ [MATE_MIXER_CHANNEL_BACK_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
+ [MATE_MIXER_CHANNEL_BACK_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
+ [MATE_MIXER_CHANNEL_BACK_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
+ [MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [MATE_MIXER_CHANNEL_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
+ [MATE_MIXER_CHANNEL_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
+ [MATE_MIXER_CHANNEL_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+ [MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
+ [MATE_MIXER_CHANNEL_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [MATE_MIXER_CHANNEL_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
+ [MATE_MIXER_CHANNEL_TOP_BACK_LEFT] = SND_MIXER_SCHN_UNKNOWN,
+ [MATE_MIXER_CHANNEL_TOP_BACK_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
+ [MATE_MIXER_CHANNEL_TOP_BACK_CENTER] = SND_MIXER_SCHN_UNKNOWN
+};
diff --git a/backends/oss4/oss4-common.h b/backends/alsa/alsa-constants.h
index fe55b2b..81257c7 100644
--- a/backends/oss4/oss4-common.h
+++ b/backends/alsa/alsa-constants.h
@@ -15,24 +15,21 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef OSS4_COMMON_H
-#define OSS4_COMMON_H
+#ifndef ALSA_CONSTANTS_H
+#define ALSA_CONSTANTS_H
-#include "config.h"
+#include <glib.h>
+#include <alsa/asoundlib.h>
+#include <libmatemixer/matemixer.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
+typedef struct {
+ gchar *name;
+ gchar *label;
+ MateMixerStreamControlRole role;
+} AlsaControlInfo;
-#ifdef HAVE_SYS_SOUNDCARD_H
-# include <sys/soundcard.h>
-#elif HAVE_SOUNDCARD_H
-# include <soundcard.h>
-#elif HAVE_MACHINE_SOUNDCARD_H
-# include <machine/soundcard.h>
-#else
-# error "No OSS4 header file present"
-#endif
+extern const AlsaControlInfo alsa_controls[];
+extern const MateMixerChannelPosition alsa_channel_map_from[];
+extern const snd_mixer_selem_channel_id_t alsa_channel_map_to[];
-#endif /* OSS4_COMMON_H */
+#endif /* ALSA_CONSTANTS_H */
diff --git a/backends/alsa/alsa-device.c b/backends/alsa/alsa-device.c
new file mode 100644
index 0000000..5acc6f5
--- /dev/null
+++ b/backends/alsa/alsa-device.c
@@ -0,0 +1,941 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <strings.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+#include <libmatemixer/matemixer.h>
+
+#include "alsa-constants.h"
+#include "alsa-device.h"
+#include "alsa-element.h"
+#include "alsa-stream.h"
+#include "alsa-stream-control.h"
+#include "alsa-stream-input-control.h"
+#include "alsa-stream-output-control.h"
+#include "alsa-switch.h"
+#include "alsa-switch-option.h"
+#include "alsa-toggle.h"
+
+#define ALSA_DEVICE_ICON "audio-card"
+
+struct _AlsaDevicePrivate
+{
+ snd_mixer_t *handle;
+ GMainContext *context;
+ GMutex mutex;
+ GCond cond;
+ AlsaStream *input;
+ AlsaStream *output;
+ GHashTable *switches;
+ gboolean events_pending;
+};
+
+enum {
+ CLOSED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0, };
+
+static void alsa_device_class_init (AlsaDeviceClass *klass);
+static void alsa_device_init (AlsaDevice *device);
+static void alsa_device_dispose (GObject *object);
+static void alsa_device_finalize (GObject *object);
+
+G_DEFINE_TYPE (AlsaDevice, alsa_device, MATE_MIXER_TYPE_DEVICE)
+
+static MateMixerSwitch *alsa_device_get_switch (MateMixerDevice *mmd,
+ const gchar *name);
+
+static GList * alsa_device_list_streams (MateMixerDevice *mmd);
+static GList * alsa_device_list_switches (MateMixerDevice *mmd);
+
+static gboolean add_stream_input_control (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+static gboolean add_stream_output_control (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+
+static gboolean add_switch (AlsaDevice *device,
+ AlsaStream *stream,
+ snd_mixer_elem_t *el);
+
+static gboolean add_device_switch (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+
+static gboolean add_stream_input_switch (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+static gboolean add_stream_output_switch (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+
+static gboolean add_stream_input_toggle (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+static gboolean add_stream_output_toggle (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+
+static void load_element (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+
+static void load_elements_by_name (AlsaDevice *device,
+ const gchar *name);
+
+static void remove_elements_by_name (AlsaDevice *device,
+ const gchar *name);
+
+static void handle_poll (AlsaDevice *device);
+
+static gboolean handle_process_events (AlsaDevice *device);
+
+static int handle_callback (snd_mixer_t *handle,
+ guint mask,
+ snd_mixer_elem_t *el);
+static int handle_element_callback (snd_mixer_elem_t *el,
+ guint mask);
+
+static void close_device (AlsaDevice *device);
+
+static gchar * get_element_name (snd_mixer_elem_t *el);
+static void get_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role);
+
+static void get_switch_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label);
+
+static void
+alsa_device_class_init (AlsaDeviceClass *klass)
+{
+ GObjectClass *object_class;
+ MateMixerDeviceClass *device_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = alsa_device_dispose;
+ object_class->finalize = alsa_device_finalize;
+
+ device_class = MATE_MIXER_DEVICE_CLASS (klass);
+ device_class->get_switch = alsa_device_get_switch;
+ device_class->list_streams = alsa_device_list_streams;
+ device_class->list_switches = alsa_device_list_switches;
+
+ signals[CLOSED] =
+ g_signal_new ("closed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (AlsaDeviceClass, closed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0,
+ G_TYPE_NONE);
+
+ g_type_class_add_private (object_class, sizeof (AlsaDevicePrivate));
+}
+
+static void
+alsa_device_init (AlsaDevice *device)
+{
+ device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
+ ALSA_TYPE_DEVICE,
+ AlsaDevicePrivate);
+
+ device->priv->switches = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ device->priv->context = g_main_context_ref_thread_default ();
+
+ g_mutex_init (&device->priv->mutex);
+ g_cond_init (&device->priv->cond);
+}
+
+static void
+alsa_device_dispose (GObject *object)
+{
+ AlsaDevice *device;
+
+ device = ALSA_DEVICE (object);
+
+ g_clear_object (&device->priv->input);
+ g_clear_object (&device->priv->output);
+
+ g_hash_table_remove_all (device->priv->switches);
+
+ G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object);
+}
+
+static void
+alsa_device_finalize (GObject *object)
+{
+ AlsaDevice *device;
+
+ device = ALSA_DEVICE (object);
+
+ g_mutex_clear (&device->priv->mutex);
+ g_cond_clear (&device->priv->cond);
+
+ g_hash_table_unref (device->priv->switches);
+ g_main_context_unref (device->priv->context);
+
+ if (device->priv->handle != NULL)
+ snd_mixer_close (device->priv->handle);
+
+ G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object);
+}
+
+AlsaDevice *
+alsa_device_new (const gchar *name, const gchar *label)
+{
+ AlsaDevice *device;
+ gchar *stream_name;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+
+ device = g_object_new (ALSA_TYPE_DEVICE,
+ "name", name,
+ "label", label,
+ "icon", ALSA_DEVICE_ICON,
+ NULL);
+
+ /* Create input and output streams, they will exist the whole time, but
+ * the added and removed signals will be emitted when the first control or
+ * switch is added or the last one removed */
+ stream_name = g_strdup_printf ("alsa-input-%s", name);
+ device->priv->input = alsa_stream_new (stream_name,
+ MATE_MIXER_DEVICE (device),
+ MATE_MIXER_STREAM_INPUT);
+ g_free (stream_name);
+
+ stream_name = g_strdup_printf ("alsa-output-%s", name);
+ device->priv->output = alsa_stream_new (stream_name,
+ MATE_MIXER_DEVICE (device),
+ MATE_MIXER_STREAM_OUTPUT);
+ g_free (stream_name);
+
+ return device;
+}
+
+gboolean
+alsa_device_open (AlsaDevice *device)
+{
+ snd_mixer_t *handle;
+ const gchar *name;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (device->priv->handle == NULL, FALSE);
+
+ name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+
+ g_debug ("Opening device %s (%s)",
+ name,
+ mate_mixer_device_get_label (MATE_MIXER_DEVICE (device)));
+
+ /* Open the mixer for the current device */
+ ret = snd_mixer_open (&handle, 0);
+ if (ret < 0) {
+ g_warning ("Failed to open mixer: %s", snd_strerror (ret));
+ return FALSE;
+ }
+ ret = snd_mixer_attach (handle, name);
+ if (ret < 0) {
+ g_warning ("Failed to attach mixer to %s: %s",
+ name,
+ snd_strerror (ret));
+
+ snd_mixer_close (handle);
+ return FALSE;
+ }
+ ret = snd_mixer_selem_register (handle, NULL, NULL);
+ if (ret < 0) {
+ g_warning ("Failed to register simple element for %s: %s",
+ name,
+ snd_strerror (ret));
+
+ snd_mixer_close (handle);
+ return FALSE;
+ }
+ ret = snd_mixer_load (handle);
+ if (ret < 0) {
+ g_warning ("Failed to load mixer elements for %s: %s",
+ name,
+ snd_strerror (ret));
+
+ snd_mixer_close (handle);
+ return FALSE;
+ }
+
+ device->priv->handle = handle;
+ return TRUE;
+}
+
+void
+alsa_device_load (AlsaDevice *device)
+{
+ GThread *thread;
+ GError *error = NULL;
+ snd_mixer_elem_t *el;
+
+ g_return_if_fail (ALSA_IS_DEVICE (device));
+ g_return_if_fail (device->priv->handle != NULL);
+
+ /* Process the mixer elements */
+ el = snd_mixer_first_elem (device->priv->handle);
+ while (el != NULL) {
+ load_element (device, el);
+
+ el = snd_mixer_elem_next (el);
+ }
+
+ /* Set callback for ALSA events */
+ snd_mixer_set_callback (device->priv->handle, handle_callback);
+ snd_mixer_set_callback_private (device->priv->handle, device);
+
+ /* Start the polling thread */
+ thread = g_thread_try_new ("matemixer-alsa-poll",
+ (GThreadFunc) handle_poll,
+ device,
+ &error);
+ if (thread == NULL) {
+ /* The error is not treated as fatal, because without the polling
+ * thread we still have most of the functionality */
+ g_warning ("Failed to create poll thread: %s", error->message);
+ g_error_free (error);
+ } else
+ g_thread_unref (thread);
+}
+
+AlsaStream *
+alsa_device_get_input_stream (AlsaDevice *device)
+{
+ g_return_val_if_fail (ALSA_IS_DEVICE (device), NULL);
+
+ /* Normally controlless streams should not exist, here we simulate the
+ * behaviour for the owning instance */
+ if (alsa_stream_is_empty (device->priv->input) == FALSE)
+ return device->priv->input;
+
+ return NULL;
+}
+
+AlsaStream *
+alsa_device_get_output_stream (AlsaDevice *device)
+{
+ g_return_val_if_fail (ALSA_IS_DEVICE (device), NULL);
+
+ /* Normally controlless streams should not exist, here we simulate the
+ * behaviour for the owning instance */
+ if (alsa_stream_is_empty (device->priv->output) == FALSE)
+ return device->priv->output;
+
+ return NULL;
+}
+
+static gboolean
+add_element (AlsaDevice *device, AlsaStream *stream, AlsaElement *element)
+{
+ gboolean added = FALSE;
+
+ if (alsa_element_load (element) == FALSE)
+ return FALSE;
+
+ if (stream != NULL) {
+ gboolean empty = FALSE;
+
+ if (alsa_stream_is_empty (stream) == TRUE) {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
+
+ /* Pretend the stream has just been created now that we are adding
+ * the first control */
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-added",
+ name);
+ empty = TRUE;
+ }
+
+ if (ALSA_IS_STREAM_CONTROL (element)) {
+ alsa_stream_add_control (stream, ALSA_STREAM_CONTROL (element));
+
+ /* If this is the first control, set it as the default one.
+ * The controls often seem to come in the order of importance, but this is
+ * driver specific, so we may later see if there is another control which
+ * better matches the default. */
+ if (empty == TRUE)
+ alsa_stream_set_default_control (stream, ALSA_STREAM_CONTROL (element));
+
+ added = TRUE;
+ } else if (ALSA_IS_SWITCH (element)) {
+ /* Switch belonging to a stream */
+ alsa_stream_add_switch (stream, ALSA_SWITCH (element));
+ added = TRUE;
+ }
+ } else if (ALSA_IS_SWITCH (element)) {
+ /* Switch belonging to the device */
+ const gchar *name =
+ mate_mixer_switch_get_name (MATE_MIXER_SWITCH (element));
+
+ g_hash_table_insert (device->priv->switches,
+ g_strdup (name),
+ g_object_ref (element));
+
+ g_signal_emit_by_name (G_OBJECT (device),
+ "switch-added",
+ name);
+ added = TRUE;
+ }
+
+ if G_LIKELY (added == TRUE) {
+ snd_mixer_elem_t *el = alsa_element_get_snd_element (element);
+
+ snd_mixer_elem_set_callback (el, handle_element_callback);
+ snd_mixer_elem_set_callback_private (el, device);
+ }
+ return added;
+}
+
+static MateMixerSwitch *
+alsa_device_get_switch (MateMixerDevice *mmd, const gchar *name)
+{
+ g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ return g_hash_table_lookup (ALSA_DEVICE (mmd)->priv->switches, name);
+}
+
+static GList *
+alsa_device_list_streams (MateMixerDevice *mmd)
+{
+ AlsaDevice *device;
+ GList *list = NULL;
+
+ g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL);
+
+ device = ALSA_DEVICE (mmd);
+
+ if (device->priv->output != NULL)
+ list = g_list_prepend (list, g_object_ref (device->priv->output));
+ if (device->priv->input != NULL)
+ list = g_list_prepend (list, g_object_ref (device->priv->input));
+
+ return list;
+}
+
+static GList *
+alsa_device_list_switches (MateMixerDevice *mmd)
+{
+ GList *list;
+
+ g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL);
+
+ /* 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 (ALSA_DEVICE (mmd)->priv->switches);
+ if (list != NULL)
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+ return list;
+}
+
+static gboolean
+add_stream_input_control (AlsaDevice *device, snd_mixer_elem_t *el)
+{
+ AlsaStreamControl *control;
+ gchar *name;
+ gchar *label;
+ MateMixerStreamControlRole role;
+
+ get_control_info (el, &name, &label, &role);
+
+ g_debug ("Found device %s input control %s",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
+ label);
+
+ control = alsa_stream_input_control_new (name, label, role);
+ g_free (name);
+ g_free (label);
+
+ alsa_element_set_snd_element (ALSA_ELEMENT (control), el);
+
+ if (add_element (device, device->priv->input, ALSA_ELEMENT (control)) == FALSE) {
+ g_object_unref (control);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+add_stream_output_control (AlsaDevice *device, snd_mixer_elem_t *el)
+{
+ AlsaStreamControl *control;
+ gchar *label;
+ gchar *name;
+ MateMixerStreamControlRole role;
+
+ get_control_info (el, &name, &label, &role);
+
+ g_debug ("Found device %s output control %s",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
+ label);
+
+ control = alsa_stream_output_control_new (name, label, role);
+ g_free (name);
+ g_free (label);
+
+ alsa_element_set_snd_element (ALSA_ELEMENT (control), el);
+
+ if (add_element (device, device->priv->output, ALSA_ELEMENT (control)) == FALSE) {
+ g_object_unref (control);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static AlsaToggle *
+create_toggle (AlsaDevice *device, snd_mixer_elem_t *el, AlsaToggleType type)
+{
+ AlsaToggle *toggle;
+ AlsaSwitchOption *on;
+ AlsaSwitchOption *off;
+ gchar *name;
+ gchar *label;
+
+ on = alsa_switch_option_new ("On", _("On"), NULL, 1);
+ off = alsa_switch_option_new ("Off", _("Off"), NULL, 0);
+
+ get_switch_info (el, &name, &label);
+ toggle = alsa_toggle_new (name,
+ label,
+ type,
+ on, off);
+ g_free (name);
+ g_free (label);
+
+ alsa_element_set_snd_element (ALSA_ELEMENT (toggle), el);
+
+ g_object_unref (on);
+ g_object_unref (off);
+
+ return toggle;
+}
+
+static gboolean
+add_switch (AlsaDevice *device, AlsaStream *stream, snd_mixer_elem_t *el)
+{
+ AlsaElement *element = NULL;
+ GList *options = NULL;
+ gchar *name;
+ gchar *label;
+ gchar item[128];
+ guint i;
+ gint count;
+ gint ret;
+
+ count = snd_mixer_selem_get_enum_items (el);
+ if G_UNLIKELY (count <= 0) {
+ g_debug ("Skipping mixer switch %s with %d items",
+ snd_mixer_selem_get_name (el),
+ count);
+ return FALSE;
+ }
+
+ for (i = 0; i < count; i++) {
+ ret = snd_mixer_selem_get_enum_item_name (el, i,
+ sizeof (item),
+ item);
+ if G_LIKELY (ret == 0)
+ options = g_list_prepend (options,
+ alsa_switch_option_new (item, item, NULL, i));
+ else
+ g_warning ("Failed to read switch item name: %s", snd_strerror (ret));
+ }
+
+ get_switch_info (el, &name, &label);
+
+ /* Takes ownership of options */
+ element = ALSA_ELEMENT (alsa_switch_new (name, label, g_list_reverse (options)));
+ g_free (name);
+ g_free (label);
+
+ alsa_element_set_snd_element (element, el);
+
+ if (add_element (device, stream, element) == FALSE) {
+ g_object_unref (element);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+add_device_switch (AlsaDevice *device, snd_mixer_elem_t *el)
+{
+ g_debug ("Reading device %s switch %s (%d items)",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
+ snd_mixer_selem_get_name (el),
+ snd_mixer_selem_get_enum_items (el));
+
+ return add_switch (device, NULL, el);
+}
+
+static gboolean
+add_stream_input_switch (AlsaDevice *device, snd_mixer_elem_t *el)
+{
+ g_debug ("Reading device %s input switch %s (%d items)",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
+ snd_mixer_selem_get_name (el),
+ snd_mixer_selem_get_enum_items (el));
+
+ return add_switch (device, device->priv->input, el);
+}
+
+static gboolean
+add_stream_output_switch (AlsaDevice *device, snd_mixer_elem_t *el)
+{
+ g_debug ("Reading device %s output switch %s (%d items)",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
+ snd_mixer_selem_get_name (el),
+ snd_mixer_selem_get_enum_items (el));
+
+ return add_switch (device, device->priv->output, el);
+}
+
+static gboolean
+add_stream_input_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
+{
+ AlsaToggle *toggle;
+
+ g_debug ("Reading device %s input toggle %s",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
+ snd_mixer_selem_get_name (el));
+
+ toggle = create_toggle (device, el, ALSA_TOGGLE_CAPTURE);
+
+ if (add_element (device, device->priv->input, ALSA_ELEMENT (toggle)) == FALSE) {
+ g_object_unref (toggle);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+add_stream_output_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
+{
+ AlsaToggle *toggle;
+
+ g_debug ("Reading device %s output toggle %s",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
+ snd_mixer_selem_get_name (el));
+
+ toggle = create_toggle (device, el, ALSA_TOGGLE_PLAYBACK);
+
+ if (add_element (device, device->priv->output, ALSA_ELEMENT (toggle)) == FALSE) {
+ g_object_unref (toggle);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+load_element (AlsaDevice *device, snd_mixer_elem_t *el)
+{
+ gboolean cvolume = FALSE;
+ gboolean pvolume = FALSE;
+
+ if (snd_mixer_selem_is_enumerated (el) == 1) {
+ gboolean cenum = FALSE;
+ gboolean penum = FALSE;
+
+ if (snd_mixer_selem_is_enum_capture (el) == 1)
+ cenum = TRUE;
+ if (snd_mixer_selem_is_enum_playback (el) == 1)
+ penum = TRUE;
+
+ /* Enumerated controls which are not marked as capture or playback
+ * are considered to be a part of the whole device, although sometimes
+ * this is incorrectly reported by the driver */
+ if (cenum == FALSE && penum == FALSE) {
+ add_device_switch (device, el);
+ }
+ else if (cenum == TRUE)
+ add_stream_input_switch (device, el);
+ else if (penum == TRUE)
+ add_stream_output_switch (device, el);
+ }
+
+ if (snd_mixer_selem_has_capture_volume (el) == 1 ||
+ snd_mixer_selem_has_common_volume (el) == 1)
+ cvolume = TRUE;
+ if (snd_mixer_selem_has_playback_volume (el) == 1 ||
+ snd_mixer_selem_has_common_volume (el) == 1)
+ pvolume = TRUE;
+
+ if (cvolume == FALSE && pvolume == FALSE) {
+ /* Control without volume and with a switch are modelled as toggles */
+ if (snd_mixer_selem_has_capture_switch (el) == 1)
+ add_stream_input_toggle (device, el);
+
+ if (snd_mixer_selem_has_playback_switch (el) == 1)
+ add_stream_output_toggle (device, el);
+ } else {
+ if (cvolume == TRUE)
+ add_stream_input_control (device, el);
+ if (pvolume == TRUE)
+ add_stream_output_control (device, el);
+ }
+}
+
+static void
+load_elements_by_name (AlsaDevice *device, const gchar *name)
+{
+ AlsaElement *element;
+
+ alsa_stream_load_elements (device->priv->input, name);
+ alsa_stream_load_elements (device->priv->output, name);
+
+ element = g_hash_table_lookup (device->priv->switches, name);
+ if (element != NULL)
+ alsa_element_load (element);
+}
+
+static void
+remove_elements_by_name (AlsaDevice *device, const gchar *name)
+{
+ if (alsa_stream_remove_elements (device->priv->input, name) == TRUE) {
+ /* Removing last stream element "removes" the stream */
+ if (alsa_stream_is_empty (device->priv->input) == TRUE) {
+ const gchar *stream_name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input));
+
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-removed",
+ stream_name);
+ }
+ }
+
+ if (alsa_stream_remove_elements (device->priv->output, name) == TRUE) {
+ /* Removing last stream element "removes" the stream */
+ if (alsa_stream_is_empty (device->priv->output) == TRUE) {
+ const gchar *stream_name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output));
+
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-removed",
+ stream_name);
+ }
+ }
+
+ if (g_hash_table_remove (device->priv->switches, name) == TRUE)
+ g_signal_emit_by_name (G_OBJECT (device),
+ "switch-removed",
+ name);
+}
+
+static void
+handle_poll (AlsaDevice *device)
+{
+ /* This function is called in a worker thread. It is supposed to wait for
+ * ALSA events and call handle_process_events(). Processing the events might
+ * result in emitting the CLOSED signal and unreffing the instance in the
+ * owner, so keep an extra reference during the lifetime of the thread. */
+ g_object_ref (device);
+
+ while (TRUE) {
+ gint ret = snd_mixer_wait (device->priv->handle, -1);
+ if (ret < 0) {
+ if (ret == EINTR)
+ continue;
+ break;
+ }
+
+ device->priv->events_pending = TRUE;
+
+ /* Process the events in the main thread because most events end up
+ * emitting signals */
+ g_main_context_invoke (device->priv->context,
+ (GSourceFunc) handle_process_events,
+ device);
+
+ g_mutex_lock (&device->priv->mutex);
+
+ /* Use a GCond to wait until the events are processed. The processing
+ * function may be called any time later in the main loop and snd_mixer_wait()
+ * returns instantly while there are pending events. Without the wait,
+ * g_main_context_invoke() could be called repeatedly to create idle sources
+ * until the first idle source function is called. */
+ while (device->priv->events_pending == TRUE)
+ g_cond_wait (&device->priv->cond, &device->priv->mutex);
+
+ g_mutex_unlock (&device->priv->mutex);
+
+ /* Exit the thread if the processing function closed the device */
+ if (device->priv->handle == NULL)
+ break;
+ }
+
+ g_debug ("Terminating poll thread for device %s",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
+
+ g_object_unref (device);
+}
+
+static gboolean
+handle_process_events (AlsaDevice *device)
+{
+ g_mutex_lock (&device->priv->mutex);
+
+ if (device->priv->handle != NULL) {
+ gint ret = snd_mixer_handle_events (device->priv->handle);
+ if (ret < 0)
+ close_device (device);
+ }
+
+ device->priv->events_pending = FALSE;
+
+ g_cond_signal (&device->priv->cond);
+ g_mutex_unlock (&device->priv->mutex);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* ALSA has a per-mixer callback and per-element callback, per-mixer callback
+ * is only used for added elements and per-element callback for all the
+ * other messages (no, the documentation doesn't say anything about that). */
+static int
+handle_callback (snd_mixer_t *handle, guint mask, snd_mixer_elem_t *el)
+{
+ if (mask & SND_CTL_EVENT_MASK_ADD) {
+ AlsaDevice *device = snd_mixer_get_callback_private (handle);
+
+ load_element (device, el);
+ }
+ return 0;
+}
+
+static int
+handle_element_callback (snd_mixer_elem_t *el, guint mask)
+{
+ AlsaDevice *device;
+ gchar *name;
+
+ device = snd_mixer_elem_get_callback_private (el);
+ name = get_element_name (el);
+
+ if (mask == SND_CTL_EVENT_MASK_REMOVE) {
+ /* Make sure this function is not called again with the element */
+ snd_mixer_elem_set_callback_private (el, NULL);
+ snd_mixer_elem_set_callback (el, NULL);
+
+ remove_elements_by_name (device, name);
+ } else {
+ if (mask & SND_CTL_EVENT_MASK_INFO) {
+ remove_elements_by_name (device, name);
+ load_element (device, el);
+ }
+ if (mask & SND_CTL_EVENT_MASK_VALUE)
+ load_elements_by_name (device, name);
+ }
+ g_free (name);
+
+ return 0;
+}
+
+static void
+close_device (AlsaDevice *device)
+{
+ if (device->priv->handle != NULL) {
+ snd_mixer_close (device->priv->handle);
+ device->priv->handle = NULL;
+ }
+
+ /* This signal tells the owner that the device has been closed voluntarily
+ * from within the instance */
+ g_signal_emit (G_OBJECT (device), signals[CLOSED], 0);
+}
+
+static gchar *
+get_element_name (snd_mixer_elem_t *el)
+{
+ return g_strdup_printf ("%s-%d",
+ snd_mixer_selem_get_name (el),
+ snd_mixer_selem_get_index (el));
+}
+
+static void
+get_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role)
+{
+ MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
+ const gchar *n;
+ const gchar *l = NULL;
+ gint i;
+
+ n = snd_mixer_selem_get_name (el);
+
+ for (i = 0; alsa_controls[i].name != NULL; i++)
+ if (strcmp (n, alsa_controls[i].name) == 0) {
+ l = alsa_controls[i].label;
+ r = alsa_controls[i].role;
+ break;
+ }
+
+ *name = get_element_name (el);
+ if (l != NULL)
+ *label = g_strdup (l);
+ else
+ *label = g_strdup (n);
+
+ *role = r;
+}
+
+static void
+get_switch_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label)
+{
+ // MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
+ const gchar *n;
+ const gchar *l = NULL;
+ // gint i;
+
+ n = snd_mixer_selem_get_name (el);
+
+ // TODO provide translated label and flags
+
+/*
+ for (i = 0; alsa_controls[i].name != NULL; i++)
+ if (strcmp (n, alsa_controls[i].name) == 0) {
+ l = alsa_controls[i].label;
+ r = alsa_controls[i].role;
+ break;
+ }
+*/
+ *name = get_element_name (el);
+ if (l != NULL)
+ *label = g_strdup (l);
+ else
+ *label = g_strdup (n);
+
+ // *role = r;
+}
diff --git a/backends/alsa/alsa-device.h b/backends/alsa/alsa-device.h
new file mode 100644
index 0000000..3b3c970
--- /dev/null
+++ b/backends/alsa/alsa-device.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_DEVICE_H
+#define ALSA_DEVICE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "alsa-stream.h"
+
+G_BEGIN_DECLS
+
+#define ALSA_TYPE_DEVICE \
+ (alsa_device_get_type ())
+#define ALSA_DEVICE(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_DEVICE, AlsaDevice))
+#define ALSA_IS_DEVICE(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_DEVICE))
+#define ALSA_DEVICE_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_DEVICE, AlsaDeviceClass))
+#define ALSA_IS_DEVICE_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_DEVICE))
+#define ALSA_DEVICE_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_DEVICE, AlsaDeviceClass))
+
+typedef struct _AlsaDevice AlsaDevice;
+typedef struct _AlsaDeviceClass AlsaDeviceClass;
+typedef struct _AlsaDevicePrivate AlsaDevicePrivate;
+
+struct _AlsaDevice
+{
+ MateMixerDevice parent;
+
+ /*< private >*/
+ AlsaDevicePrivate *priv;
+};
+
+struct _AlsaDeviceClass
+{
+ MateMixerDeviceClass parent_class;
+
+ /*< private >*/
+ void (*closed) (AlsaDevice *device);
+};
+
+GType alsa_device_get_type (void) G_GNUC_CONST;
+
+AlsaDevice *alsa_device_new (const gchar *name,
+ const gchar *label);
+
+gboolean alsa_device_open (AlsaDevice *device);
+void alsa_device_load (AlsaDevice *device);
+
+AlsaStream *alsa_device_get_input_stream (AlsaDevice *device);
+AlsaStream *alsa_device_get_output_stream (AlsaDevice *device);
+
+G_END_DECLS
+
+#endif /* ALSA_DEVICE_H */
diff --git a/backends/alsa/alsa-element.c b/backends/alsa/alsa-element.c
new file mode 100644
index 0000000..f925064
--- /dev/null
+++ b/backends/alsa/alsa-element.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+
+#include "alsa-element.h"
+
+G_DEFINE_INTERFACE (AlsaElement, alsa_element, G_TYPE_OBJECT)
+
+static void
+alsa_element_default_init (AlsaElementInterface *iface)
+{
+}
+
+snd_mixer_elem_t *
+alsa_element_get_snd_element (AlsaElement *element)
+{
+ g_return_val_if_fail (ALSA_IS_ELEMENT (element), NULL);
+
+ return ALSA_ELEMENT_GET_INTERFACE (element)->get_snd_element (element);
+}
+
+void
+alsa_element_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
+{
+ g_return_if_fail (ALSA_IS_ELEMENT (element));
+
+ ALSA_ELEMENT_GET_INTERFACE (element)->set_snd_element (element, el);
+}
+
+gboolean
+alsa_element_load (AlsaElement *element)
+{
+ g_return_val_if_fail (ALSA_IS_ELEMENT (element), FALSE);
+
+ return ALSA_ELEMENT_GET_INTERFACE (element)->load (element);
+}
diff --git a/backends/alsa/alsa-element.h b/backends/alsa/alsa-element.h
new file mode 100644
index 0000000..01d30f1
--- /dev/null
+++ b/backends/alsa/alsa-element.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_ELEMENT_H
+#define ALSA_ELEMENT_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+
+G_BEGIN_DECLS
+
+#define ALSA_TYPE_ELEMENT \
+ (alsa_element_get_type ())
+#define ALSA_ELEMENT(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_ELEMENT, AlsaElement))
+#define ALSA_IS_ELEMENT(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_ELEMENT))
+#define ALSA_ELEMENT_GET_INTERFACE(o) \
+ (G_TYPE_INSTANCE_GET_INTERFACE ((o), ALSA_TYPE_ELEMENT, AlsaElementInterface))
+
+typedef struct _AlsaElement AlsaElement; /* dummy object */
+typedef struct _AlsaElementInterface AlsaElementInterface;
+
+struct _AlsaElementInterface
+{
+ GTypeInterface parent_iface;
+
+ /*< private >*/
+ snd_mixer_elem_t *(*get_snd_element) (AlsaElement *element);
+ void (*set_snd_element) (AlsaElement *element,
+ snd_mixer_elem_t *el);
+
+ gboolean (*load) (AlsaElement *element);
+};
+
+GType alsa_element_get_type (void) G_GNUC_CONST;
+
+snd_mixer_elem_t *alsa_element_get_snd_element (AlsaElement *element);
+void alsa_element_set_snd_element (AlsaElement *element,
+ snd_mixer_elem_t *el);
+
+gboolean alsa_element_load (AlsaElement *element);
+
+G_END_DECLS
+
+#endif /* ALSA_ELEMENT_H */
diff --git a/backends/alsa/alsa-stream-control.c b/backends/alsa/alsa-stream-control.c
new file mode 100644
index 0000000..bc7a937
--- /dev/null
+++ b/backends/alsa/alsa-stream-control.c
@@ -0,0 +1,739 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "alsa-constants.h"
+#include "alsa-element.h"
+#include "alsa-stream-control.h"
+
+struct _AlsaStreamControlPrivate
+{
+ AlsaControlData data;
+ guint32 channel_mask;
+ snd_mixer_elem_t *element;
+};
+
+static void alsa_element_interface_init (AlsaElementInterface *iface);
+
+static void alsa_stream_control_class_init (AlsaStreamControlClass *klass);
+static void alsa_stream_control_init (AlsaStreamControl *control);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (AlsaStreamControl, alsa_stream_control,
+ MATE_MIXER_TYPE_STREAM_CONTROL,
+ G_IMPLEMENT_INTERFACE (ALSA_TYPE_ELEMENT,
+ alsa_element_interface_init))
+
+static snd_mixer_elem_t * alsa_stream_control_get_snd_element (AlsaElement *element);
+static void alsa_stream_control_set_snd_element (AlsaElement *element,
+ snd_mixer_elem_t *el);
+
+static gboolean alsa_stream_control_load (AlsaElement *element);
+
+static gboolean alsa_stream_control_set_mute (MateMixerStreamControl *mmsc,
+ gboolean mute);
+
+static guint alsa_stream_control_get_num_channels (MateMixerStreamControl *mmsc);
+
+static guint alsa_stream_control_get_volume (MateMixerStreamControl *mmsc);
+
+static gboolean alsa_stream_control_set_volume (MateMixerStreamControl *mmsc,
+ guint volume);
+
+static gdouble alsa_stream_control_get_decibel (MateMixerStreamControl *mmsc);
+
+static gboolean alsa_stream_control_set_decibel (MateMixerStreamControl *mmsc,
+ gdouble decibel);
+
+static gboolean alsa_stream_control_has_channel_position (MateMixerStreamControl *mmsc,
+ MateMixerChannelPosition position);
+static MateMixerChannelPosition alsa_stream_control_get_channel_position (MateMixerStreamControl *mmsc,
+ guint channel);
+
+static guint alsa_stream_control_get_channel_volume (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean alsa_stream_control_set_channel_volume (MateMixerStreamControl *mmsc,
+ guint channel,
+ guint volume);
+
+static gdouble alsa_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean alsa_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc,
+ guint channel,
+ gdouble decibel);
+
+static gboolean alsa_stream_control_set_balance (MateMixerStreamControl *mmsc,
+ gfloat balance);
+
+static gboolean alsa_stream_control_set_fade (MateMixerStreamControl *mmsc,
+ gfloat fade);
+
+static guint alsa_stream_control_get_min_volume (MateMixerStreamControl *mmsc);
+static guint alsa_stream_control_get_max_volume (MateMixerStreamControl *mmsc);
+static guint alsa_stream_control_get_normal_volume (MateMixerStreamControl *mmsc);
+static guint alsa_stream_control_get_base_volume (MateMixerStreamControl *mmsc);
+
+static void control_data_get_average_left_right (AlsaControlData *data,
+ guint *left,
+ guint *right);
+static void control_data_get_average_front_back (AlsaControlData *data,
+ guint *front,
+ guint *back);
+
+static gfloat control_data_get_balance (AlsaControlData *data);
+static gfloat control_data_get_fade (AlsaControlData *data);
+
+static void
+alsa_element_interface_init (AlsaElementInterface *iface)
+{
+ iface->get_snd_element = alsa_stream_control_get_snd_element;
+ iface->set_snd_element = alsa_stream_control_set_snd_element;
+ iface->load = alsa_stream_control_load;
+}
+
+static void
+alsa_stream_control_class_init (AlsaStreamControlClass *klass)
+{
+ MateMixerStreamControlClass *control_class;
+
+ control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
+
+ control_class->set_mute = alsa_stream_control_set_mute;
+ control_class->get_num_channels = alsa_stream_control_get_num_channels;
+ control_class->get_volume = alsa_stream_control_get_volume;
+ control_class->set_volume = alsa_stream_control_set_volume;
+ control_class->get_decibel = alsa_stream_control_get_decibel;
+ control_class->set_decibel = alsa_stream_control_set_decibel;
+ control_class->has_channel_position = alsa_stream_control_has_channel_position;
+ control_class->get_channel_position = alsa_stream_control_get_channel_position;
+ control_class->get_channel_volume = alsa_stream_control_get_channel_volume;
+ control_class->set_channel_volume = alsa_stream_control_set_channel_volume;
+ control_class->get_channel_decibel = alsa_stream_control_get_channel_decibel;
+ control_class->set_channel_decibel = alsa_stream_control_set_channel_decibel;
+ control_class->set_balance = alsa_stream_control_set_balance;
+ control_class->set_fade = alsa_stream_control_set_fade;
+ control_class->get_min_volume = alsa_stream_control_get_min_volume;
+ control_class->get_max_volume = alsa_stream_control_get_max_volume;
+ control_class->get_normal_volume = alsa_stream_control_get_normal_volume;
+ control_class->get_base_volume = alsa_stream_control_get_base_volume;
+
+ g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (AlsaStreamControlPrivate));
+}
+
+static void
+alsa_stream_control_init (AlsaStreamControl *control)
+{
+ control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control,
+ ALSA_TYPE_STREAM_CONTROL,
+ AlsaStreamControlPrivate);
+}
+
+AlsaControlData *
+alsa_stream_control_get_data (AlsaStreamControl *control)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), NULL);
+
+ return &control->priv->data;
+}
+
+void
+alsa_stream_control_set_data (AlsaStreamControl *control, AlsaControlData *data)
+{
+ MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS;
+ MateMixerStreamControl *mmsc;
+
+ g_return_if_fail (ALSA_IS_STREAM_CONTROL (control));
+ g_return_if_fail (data != NULL);
+
+ mmsc = MATE_MIXER_STREAM_CONTROL (control);
+
+ g_object_freeze_notify (G_OBJECT (control));
+
+ if (data->channels > 0) {
+ if (data->switch_usable == TRUE) {
+ flags |= MATE_MIXER_STREAM_CONTROL_HAS_MUTE;
+ if (data->active == TRUE)
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_MUTE;
+ }
+ flags |= MATE_MIXER_STREAM_CONTROL_HAS_VOLUME;
+ if (data->active == TRUE)
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME;
+ }
+ if (data->max_decibel > -MATE_MIXER_INFINITY)
+ flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
+
+ control->priv->data = *data;
+ control->priv->channel_mask = _mate_mixer_create_channel_mask (data->c, data->channels);
+
+ if (data->volume_joined == FALSE) {
+ if (MATE_MIXER_CHANNEL_MASK_HAS_LEFT (control->priv->channel_mask) &&
+ MATE_MIXER_CHANNEL_MASK_HAS_RIGHT (control->priv->channel_mask))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+
+ if (MATE_MIXER_CHANNEL_MASK_HAS_FRONT (control->priv->channel_mask) &&
+ MATE_MIXER_CHANNEL_MASK_HAS_BACK (control->priv->channel_mask))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+ }
+
+ _mate_mixer_stream_control_set_flags (mmsc, flags);
+
+ if (data->switch_usable == TRUE) {
+ gboolean mute;
+
+ /* If the mute switch is joined, all the channels get the same value,
+ * otherwise the element has per-channel mute, which we don't support.
+ * In that case, treat the control as unmuted if any channel is
+ * unmuted. */
+ if (data->channels == 1 || data->switch_joined == TRUE) {
+ mute = data->m[0];
+ } else {
+ gint i;
+ mute = TRUE;
+ for (i = 0; i < data->channels; i++)
+ if (data->m[i] == FALSE) {
+ mute = FALSE;
+ break;
+ }
+ }
+ _mate_mixer_stream_control_set_mute (mmsc, mute);
+ } else
+ _mate_mixer_stream_control_set_mute (mmsc, FALSE);
+
+ if (flags & MATE_MIXER_STREAM_CONTROL_CAN_BALANCE)
+ _mate_mixer_stream_control_set_balance (mmsc, control_data_get_balance (data));
+ if (flags & MATE_MIXER_STREAM_CONTROL_CAN_FADE)
+ _mate_mixer_stream_control_set_fade (mmsc, control_data_get_fade (data));
+
+ g_object_thaw_notify (G_OBJECT (control));
+}
+
+static snd_mixer_elem_t *
+alsa_stream_control_get_snd_element (AlsaElement *element)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (element), NULL);
+
+ return ALSA_STREAM_CONTROL (element)->priv->element;
+}
+
+static void
+alsa_stream_control_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
+{
+ g_return_if_fail (ALSA_IS_STREAM_CONTROL (element));
+ g_return_if_fail (el != NULL);
+
+ ALSA_STREAM_CONTROL (element)->priv->element = el;
+}
+
+static gboolean
+alsa_stream_control_load (AlsaElement *element)
+{
+ AlsaStreamControl *control;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (element), FALSE);
+
+ control = ALSA_STREAM_CONTROL (element);
+
+ return ALSA_STREAM_CONTROL_GET_CLASS (control)->load (control);
+}
+
+static gboolean
+alsa_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute)
+{
+ AlsaStreamControl *control;
+ gboolean change = FALSE;
+ gint i;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+
+ /* If the switch is joined, only verify the first channel */
+ if (control->priv->data.switch_joined == TRUE) {
+ if (control->priv->data.m[0] != mute)
+ change = TRUE;
+ } else {
+ /* Avoid trying to set the mute if all channels are already at the
+ * selected mute value */
+ for (i = 0; i < control->priv->data.channels; i++)
+ if (control->priv->data.m[i] != mute) {
+ change = TRUE;
+ break;
+ }
+ }
+
+ if (change == TRUE) {
+ AlsaStreamControlClass *klass;
+
+ klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
+ if (klass->set_mute (control, mute) == FALSE)
+ return FALSE;
+
+ for (i = 0; i < control->priv->data.channels; i++)
+ control->priv->data.m[i] = mute;
+ }
+ return TRUE;
+}
+
+static guint
+alsa_stream_control_get_num_channels (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), 0);
+
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.channels;
+}
+
+static guint
+alsa_stream_control_get_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), 0);
+
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.volume;
+}
+
+static gboolean
+alsa_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume)
+{
+ AlsaStreamControl *control;
+ gboolean change = FALSE;
+ gint i;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+ volume = CLAMP (volume, control->priv->data.min, control->priv->data.max);
+
+ /* If the volume is joined, only verify the first channel */
+ if (control->priv->data.volume_joined == TRUE) {
+ if (control->priv->data.v[0] != volume)
+ change = TRUE;
+ } else {
+ /* Avoid trying to set the volume if all channels are already at the
+ * selected volume */
+ for (i = 0; i < control->priv->data.channels; i++)
+ if (control->priv->data.v[i] != volume) {
+ change = TRUE;
+ break;
+ }
+ }
+
+ if (change == TRUE) {
+ AlsaStreamControlClass *klass;
+
+ klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
+ if (klass->set_volume (control, volume) == FALSE)
+ return FALSE;
+
+ for (i = 0; i < control->priv->data.channels; i++)
+ control->priv->data.v[i] = volume;
+
+ g_object_notify (G_OBJECT (control), "volume");
+ }
+ return TRUE;
+}
+
+static gdouble
+alsa_stream_control_get_decibel (MateMixerStreamControl *mmsc)
+{
+ AlsaStreamControl *control;
+ AlsaStreamControlClass *klass;
+ guint volume;
+ gdouble decibel;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+ klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
+ volume = alsa_stream_control_get_volume (mmsc);
+
+ if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE)
+ return FALSE;
+
+ return decibel;
+}
+
+static gboolean
+alsa_stream_control_set_decibel (MateMixerStreamControl *mmsc, gdouble decibel)
+{
+ AlsaStreamControl *control;
+ AlsaStreamControlClass *klass;
+ guint volume;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+ klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
+
+ if (klass->get_volume_from_decibel (control, decibel, &volume) == FALSE)
+ return FALSE;
+
+ return alsa_stream_control_set_volume (mmsc, volume);
+}
+
+static gboolean
+alsa_stream_control_has_channel_position (MateMixerStreamControl *mmsc,
+ MateMixerChannelPosition position)
+{
+ AlsaStreamControl *control;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+
+ if MATE_MIXER_CHANNEL_MASK_HAS_CHANNEL (control->priv->channel_mask, position)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static MateMixerChannelPosition
+alsa_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel)
+{
+ AlsaStreamControl *control;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->data.channels)
+ return MATE_MIXER_CHANNEL_UNKNOWN;
+
+ return control->priv->data.c[channel];
+}
+
+static guint
+alsa_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel)
+{
+ AlsaStreamControl *control;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), 0);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->data.channels)
+ return FALSE;
+
+ return control->priv->data.v[channel];
+}
+
+static gboolean
+alsa_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume)
+{
+ AlsaStreamControl *control;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->data.channels)
+ return FALSE;
+
+ /* Set volume for all channels at once when channels are joined */
+ if (control->priv->data.volume_joined == TRUE)
+ return alsa_stream_control_set_volume (mmsc, volume);
+
+ if (volume != control->priv->data.v[channel]) {
+ AlsaStreamControlClass *klass;
+
+ /* Convert channel index to ALSA channel position and make sure it is valid */
+ snd_mixer_selem_channel_id_t c = alsa_channel_map_to[control->priv->data.c[channel]];
+ if G_UNLIKELY (c == SND_MIXER_SCHN_UNKNOWN) {
+ g_warn_if_reached ();
+ return FALSE;
+ }
+
+ klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
+ if (klass->set_channel_volume (control, c, volume) == FALSE)
+ return FALSE;
+
+ control->priv->data.v[channel] = volume;
+
+ g_object_notify (G_OBJECT (control), "volume");
+ }
+ return TRUE;
+}
+
+static gdouble
+alsa_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint channel)
+{
+ AlsaStreamControl *control;
+ AlsaStreamControlClass *klass;
+ guint volume;
+ gdouble decibel;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->data.channels)
+ return FALSE;
+
+ klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
+ volume = control->priv->data.v[channel];
+
+ if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE)
+ return FALSE;
+
+ return decibel;
+}
+
+static gboolean
+alsa_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc,
+ guint channel,
+ gdouble decibel)
+{
+ AlsaStreamControl *control;
+ AlsaStreamControlClass *klass;
+ guint volume;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+ klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
+
+ if (klass->get_volume_from_decibel (control, decibel, &volume) == FALSE)
+ return FALSE;
+
+ return alsa_stream_control_set_channel_volume (mmsc, channel, volume);
+}
+
+static gboolean
+alsa_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance)
+{
+ AlsaStreamControlClass *klass;
+ AlsaStreamControl *control;
+ AlsaControlData *data;
+ guint left,
+ right;
+ guint nleft,
+ nright;
+ guint max;
+ guint channel;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+ klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
+
+ data = &control->priv->data;
+ control_data_get_average_left_right (data, &left, &right);
+
+ max = MAX (left, right);
+ if (balance <= 0) {
+ nright = (balance + 1.0f) * max;
+ nleft = max;
+ } else {
+ nleft = (1.0f - balance) * max;
+ nright = max;
+ }
+
+ for (channel = 0; channel < data->channels; channel++) {
+ gboolean lc = MATE_MIXER_IS_LEFT_CHANNEL (data->c[channel]);
+ gboolean rc = MATE_MIXER_IS_RIGHT_CHANNEL (data->c[channel]);
+
+ if (lc == TRUE || rc == TRUE) {
+ guint volume;
+ if (lc == TRUE) {
+ if (left == 0)
+ volume = nleft;
+ else
+ volume = CLAMP (((guint64) data->v[channel] * (guint64) nleft) / (guint64) left,
+ data->min,
+ data->max);
+ } else {
+ if (right == 0)
+ volume = nright;
+ else
+ volume = CLAMP (((guint64) data->v[channel] * (guint64) nright) / (guint64) right,
+ data->min,
+ data->max);
+ }
+
+ if (klass->set_channel_volume (control,
+ alsa_channel_map_to[data->c[channel]],
+ volume) == TRUE)
+ data->v[channel] = volume;
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade)
+{
+ AlsaStreamControlClass *klass;
+ AlsaStreamControl *control;
+ AlsaControlData *data;
+ guint front,
+ back;
+ guint nfront,
+ nback;
+ guint max;
+ guint channel;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = ALSA_STREAM_CONTROL (mmsc);
+ klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
+
+ data = &control->priv->data;
+ control_data_get_average_front_back (data, &front, &back);
+
+ max = MAX (front, back);
+ if (fade <= 0) {
+ nback = (fade + 1.0f) * max;
+ nfront = max;
+ } else {
+ nfront = (1.0f - fade) * max;
+ nback = max;
+ }
+
+ for (channel = 0; channel < data->channels; channel++) {
+ gboolean fc = MATE_MIXER_IS_FRONT_CHANNEL (data->c[channel]);
+ gboolean bc = MATE_MIXER_IS_BACK_CHANNEL (data->c[channel]);
+
+ if (fc == TRUE || bc == TRUE) {
+ guint volume;
+ if (fc == TRUE) {
+ if (front == 0)
+ volume = nfront;
+ else
+ volume = CLAMP (((guint64) data->v[channel] * (guint64) nfront) / (guint64) front,
+ data->min,
+ data->max);
+ } else {
+ if (back == 0)
+ volume = nback;
+ else
+ volume = CLAMP (((guint64) data->v[channel] * (guint64) nback) / (guint64) back,
+ data->min,
+ data->max);
+ }
+
+ if (klass->set_channel_volume (control,
+ alsa_channel_map_to[data->c[channel]],
+ volume) == TRUE)
+ data->v[channel] = volume;
+ }
+ }
+ return TRUE;
+}
+
+static guint
+alsa_stream_control_get_min_volume (MateMixerStreamControl *msc)
+{
+ return ALSA_STREAM_CONTROL (msc)->priv->data.min;
+}
+
+static guint
+alsa_stream_control_get_max_volume (MateMixerStreamControl *msc)
+{
+ return ALSA_STREAM_CONTROL (msc)->priv->data.max;
+}
+
+static guint
+alsa_stream_control_get_normal_volume (MateMixerStreamControl *msc)
+{
+ return ALSA_STREAM_CONTROL (msc)->priv->data.max;
+}
+
+static guint
+alsa_stream_control_get_base_volume (MateMixerStreamControl *msc)
+{
+ return ALSA_STREAM_CONTROL (msc)->priv->data.max;
+}
+
+static void
+control_data_get_average_left_right (AlsaControlData *data, guint *left, guint *right)
+{
+ guint l = 0,
+ r = 0;
+ guint nl = 0,
+ nr = 0;
+ guint channel;
+
+ for (channel = 0; channel < data->channels; channel++)
+ if MATE_MIXER_IS_LEFT_CHANNEL (data->c[channel]) {
+ l += data->v[channel];
+ nl++;
+ }
+ else if MATE_MIXER_IS_RIGHT_CHANNEL (data->c[channel]) {
+ r += data->v[channel];
+ nr++;
+ }
+
+ *left = (nl > 0) ? l / nl : data->max;
+ *right = (nr > 0) ? r / nr : data->max;
+}
+
+static void
+control_data_get_average_front_back (AlsaControlData *data, guint *front, guint *back)
+{
+ guint f = 0,
+ b = 0;
+ guint nf = 0,
+ nb = 0;
+ guint channel;
+
+ for (channel = 0; channel < data->channels; channel++)
+ if MATE_MIXER_IS_FRONT_CHANNEL (data->c[channel]) {
+ f += data->v[channel];
+ nf++;
+ }
+ else if MATE_MIXER_IS_RIGHT_CHANNEL (data->c[channel]) {
+ b += data->v[channel];
+ nb++;
+ }
+
+ *front = (nf > 0) ? f / nf : data->max;
+ *back = (nb > 0) ? b / nb : data->max;
+}
+
+static gfloat
+control_data_get_balance (AlsaControlData *data)
+{
+ guint left;
+ guint right;
+
+ control_data_get_average_left_right (data, &left, &right);
+ if (left == right)
+ return 0.0f;
+
+ if (left > right)
+ return -1.0f + ((gfloat) right / (gfloat) left);
+ else
+ return +1.0f - ((gfloat) left / (gfloat) right);
+}
+
+static gfloat
+control_data_get_fade (AlsaControlData *data)
+{
+ guint front;
+ guint back;
+
+ control_data_get_average_front_back (data, &front, &back);
+ if (front == back)
+ return 0.0f;
+
+ if (front > back)
+ return -1.0f + ((gfloat) back / (gfloat) front);
+ else
+ return +1.0f - ((gfloat) front / (gfloat) back);
+}
diff --git a/backends/alsa/alsa-stream-control.h b/backends/alsa/alsa-stream-control.h
new file mode 100644
index 0000000..f9ac6b6
--- /dev/null
+++ b/backends/alsa/alsa-stream-control.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_STREAM_CONTROL_H
+#define ALSA_STREAM_CONTROL_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+#include <libmatemixer/matemixer.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ gboolean active;
+ MateMixerChannelPosition c[MATE_MIXER_CHANNEL_MAX];
+ guint v[MATE_MIXER_CHANNEL_MAX];
+ gboolean m[MATE_MIXER_CHANNEL_MAX];
+ guint volume;
+ gboolean volume_joined;
+ gboolean switch_usable;
+ gboolean switch_joined;
+ guint min;
+ guint max;
+ gdouble min_decibel;
+ gdouble max_decibel;
+ guint channels;
+} AlsaControlData;
+
+extern const MateMixerChannelPosition alsa_channel_map_from[SND_MIXER_SCHN_LAST];
+extern const snd_mixer_selem_channel_id_t alsa_channel_map_to[MATE_MIXER_CHANNEL_MAX];
+
+#define ALSA_TYPE_STREAM_CONTROL \
+ (alsa_stream_control_get_type ())
+#define ALSA_STREAM_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_STREAM_CONTROL, AlsaStreamControl))
+#define ALSA_IS_STREAM_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_STREAM_CONTROL))
+#define ALSA_STREAM_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_STREAM_CONTROL, AlsaStreamControlClass))
+#define ALSA_IS_STREAM_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_STREAM_CONTROL))
+#define ALSA_STREAM_CONTROL_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_STREAM_CONTROL, AlsaStreamControlClass))
+
+typedef struct _AlsaStreamControl AlsaStreamControl;
+typedef struct _AlsaStreamControlClass AlsaStreamControlClass;
+typedef struct _AlsaStreamControlPrivate AlsaStreamControlPrivate;
+
+struct _AlsaStreamControl
+{
+ MateMixerStreamControl parent;
+
+ /*< private >*/
+ AlsaStreamControlPrivate *priv;
+
+};
+
+struct _AlsaStreamControlClass
+{
+ MateMixerStreamControlClass parent_class;
+
+ /*< private >*/
+ gboolean (*load) (AlsaStreamControl *control);
+
+ gboolean (*set_mute) (AlsaStreamControl *control,
+ gboolean mute);
+
+ gboolean (*set_volume) (AlsaStreamControl *control,
+ guint volume);
+
+ gboolean (*set_channel_volume) (AlsaStreamControl *control,
+ snd_mixer_selem_channel_id_t channel,
+ guint volume);
+
+ gboolean (*get_volume_from_decibel) (AlsaStreamControl *control,
+ gdouble decibel,
+ guint *volume);
+
+ gboolean (*get_decibel_from_volume) (AlsaStreamControl *control,
+ guint volume,
+ gdouble *decibel);
+};
+
+GType alsa_stream_control_get_type (void) G_GNUC_CONST;
+
+AlsaControlData * alsa_stream_control_get_data (AlsaStreamControl *control);
+
+void alsa_stream_control_set_data (AlsaStreamControl *control,
+ AlsaControlData *data);
+
+gboolean alsa_stream_control_set_role (AlsaStreamControl *control,
+ MateMixerStreamControlRole role);
+
+G_END_DECLS
+
+#endif /* ALSA_STREAM_CONTROL_H */
diff --git a/backends/alsa/alsa-stream-input-control.c b/backends/alsa/alsa-stream-input-control.c
new file mode 100644
index 0000000..2ef0c42
--- /dev/null
+++ b/backends/alsa/alsa-stream-input-control.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "alsa-element.h"
+#include "alsa-stream-control.h"
+#include "alsa-stream-input-control.h"
+
+static void alsa_stream_input_control_class_init (AlsaStreamInputControlClass *klass);
+static void alsa_stream_input_control_init (AlsaStreamInputControl *control);
+
+G_DEFINE_TYPE (AlsaStreamInputControl, alsa_stream_input_control, ALSA_TYPE_STREAM_CONTROL)
+
+static gboolean alsa_stream_input_control_load (AlsaStreamControl *control);
+
+static gboolean alsa_stream_input_control_set_mute (AlsaStreamControl *control,
+ gboolean mute);
+
+static gboolean alsa_stream_input_control_set_volume (AlsaStreamControl *control,
+ guint volume);
+
+static gboolean alsa_stream_input_control_set_channel_volume (AlsaStreamControl *control,
+ snd_mixer_selem_channel_id_t channel,
+ guint volume);
+
+static gboolean alsa_stream_input_control_get_volume_from_decibel (AlsaStreamControl *control,
+ gdouble decibel,
+ guint *volume);
+
+static gboolean alsa_stream_input_control_get_decibel_from_volume (AlsaStreamControl *control,
+ guint volume,
+ gdouble *decibel);
+
+static void read_volume_data (snd_mixer_elem_t *el,
+ AlsaControlData *data);
+
+static void
+alsa_stream_input_control_class_init (AlsaStreamInputControlClass *klass)
+{
+ AlsaStreamControlClass *control_class;
+
+ control_class = ALSA_STREAM_CONTROL_CLASS (klass);
+
+ control_class->load = alsa_stream_input_control_load;
+ control_class->set_mute = alsa_stream_input_control_set_mute;
+ control_class->set_volume = alsa_stream_input_control_set_volume;
+ control_class->set_channel_volume = alsa_stream_input_control_set_channel_volume;
+ control_class->get_volume_from_decibel = alsa_stream_input_control_get_volume_from_decibel;
+ control_class->get_decibel_from_volume = alsa_stream_input_control_get_decibel_from_volume;
+}
+
+static void
+alsa_stream_input_control_init (AlsaStreamInputControl *control)
+{
+}
+
+AlsaStreamControl *
+alsa_stream_input_control_new (const gchar *name,
+ const gchar *label,
+ MateMixerStreamControlRole role)
+{
+ return g_object_new (ALSA_TYPE_STREAM_INPUT_CONTROL,
+ "name", name,
+ "label", label,
+ "role", role,
+ NULL);
+}
+
+static gboolean
+alsa_stream_input_control_load (AlsaStreamControl *control)
+{
+ AlsaControlData data;
+ snd_mixer_elem_t *el;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_INPUT_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ /* Expect that the element has a volume control */
+ if G_UNLIKELY (snd_mixer_selem_has_capture_volume (el) == 0 &&
+ snd_mixer_selem_has_common_volume (el) == 0) {
+ g_warn_if_reached ();
+ return FALSE;
+ }
+
+ memset (&data, 0, sizeof (AlsaControlData));
+
+ /* We model any control switch as mute */
+ if (snd_mixer_selem_has_capture_switch (el) == 1 ||
+ snd_mixer_selem_has_common_switch (el) == 1)
+ data.switch_usable = TRUE;
+
+ data.active = snd_mixer_selem_is_active (el);
+
+ /* Read the volume data but do not error out if it fails, since ALSA reports
+ * the control to have a volume, expect the control to match what we need - slider
+ * with an optional mute toggle.
+ * If it fails to read the volume data, just treat it as a volumeless control */
+ read_volume_data (el, &data);
+
+ alsa_stream_control_set_data (control, &data);
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_input_control_set_mute (AlsaStreamControl *control, gboolean mute)
+{
+ snd_mixer_elem_t *el;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ /* Set the switch for all channels */
+ ret = snd_mixer_selem_set_capture_switch_all (el, !mute);
+ if (ret < 0) {
+ g_warning ("Failed to set capture switch: %s", snd_strerror (ret));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_input_control_set_volume (AlsaStreamControl *control, guint volume)
+{
+ snd_mixer_elem_t *el;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ /* Set the volume for all channels */
+ ret = snd_mixer_selem_set_capture_volume_all (el, volume);
+ if (ret < 0) {
+ g_warning ("Failed to set volume: %s", snd_strerror (ret));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_input_control_set_channel_volume (AlsaStreamControl *control,
+ snd_mixer_selem_channel_id_t channel,
+ guint volume)
+{
+ snd_mixer_elem_t *el;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ /* Set the volume for a single channels, the volume may still be "joined" and
+ * set all the channels by itself */
+ ret = snd_mixer_selem_set_capture_volume (el, channel, volume);
+ if (ret < 0) {
+ g_warning ("Failed to set channel volume: %s", snd_strerror (ret));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_input_control_get_volume_from_decibel (AlsaStreamControl *control,
+ gdouble decibel,
+ guint *volume)
+{
+ snd_mixer_elem_t *el;
+ glong value;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ ret = snd_mixer_selem_ask_capture_dB_vol (el, (glong) (decibel * 100), 0, &value);
+ if (ret < 0) {
+ g_warning ("Failed to convert volume: %s", snd_strerror (ret));
+ return FALSE;
+ }
+
+ *volume = value;
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_input_control_get_decibel_from_volume (AlsaStreamControl *control,
+ guint volume,
+ gdouble *decibel)
+{
+ snd_mixer_elem_t *el;
+ glong value;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ ret = snd_mixer_selem_ask_capture_vol_dB (el, (glong) volume, &value);
+ if (ret < 0) {
+ g_warning ("Failed to convert volume: %s", snd_strerror (ret));
+ return FALSE;
+ }
+
+ *decibel = value / 100.0;
+ return TRUE;
+}
+
+static void
+read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data)
+{
+ glong volume;
+ glong min, max;
+ gint ret;
+ gint i;
+
+ /* Read volume ranges, this call should never fail on valid input */
+ ret = snd_mixer_selem_get_capture_volume_range (el, &min, &max);
+ if G_UNLIKELY (ret < 0) {
+ g_warning ("Failed to read capture volume range: %s", snd_strerror (ret));
+ return;
+ }
+ data->min = (guint) min;
+ data->max = (guint) max;
+
+ /* This fails when decibels are not supported */
+ ret = snd_mixer_selem_get_capture_dB_range (el, &min, &max);
+ if (ret == 0) {
+ data->min_decibel = min / 100.0;
+ data->max_decibel = max / 100.0;
+ } else
+ data->min_decibel = data->max_decibel = -MATE_MIXER_INFINITY;
+
+ for (i = 0; i < MATE_MIXER_CHANNEL_MAX; i++)
+ data->v[i] = data->min;
+
+ data->volume = data->min;
+ data->volume_joined = snd_mixer_selem_has_capture_volume_joined (el);
+
+ if (data->switch_usable == TRUE)
+ data->switch_joined = snd_mixer_selem_has_capture_switch_joined (el);
+
+ if (snd_mixer_selem_is_capture_mono (el) == 1) {
+ /* Special handling for single channel controls */
+ ret = snd_mixer_selem_get_capture_volume (el, SND_MIXER_SCHN_MONO, &volume);
+ if (ret == 0) {
+ data->channels = 1;
+
+ data->c[0] = MATE_MIXER_CHANNEL_MONO;
+ data->v[0] = data->volume = (guint) volume;
+ } else {
+ g_warning ("Failed to read capture volume: %s", snd_strerror (ret));
+ }
+
+ if (data->switch_usable == TRUE) {
+ gint value;
+
+ ret = snd_mixer_selem_get_capture_switch (el, SND_MIXER_SCHN_MONO, &value);
+ if G_LIKELY (ret == 0)
+ data->m[0] = !value;
+ }
+ } else {
+ snd_mixer_selem_channel_id_t channel;
+
+ /* We use numeric channel indices, but ALSA only works with channel
+ * positions, go over all the positions supported by ALSA and create
+ * a list of channels */
+ for (channel = 0; channel < SND_MIXER_SCHN_LAST; channel++) {
+ if (snd_mixer_selem_has_capture_channel (el, channel) == 0)
+ continue;
+
+ if (data->switch_usable == TRUE) {
+ gint value;
+
+ ret = snd_mixer_selem_get_capture_switch (el, channel, &value);
+ if (ret == 0)
+ data->m[channel] = !value;
+ }
+
+ ret = snd_mixer_selem_get_capture_volume (el, channel, &volume);
+ if (ret < 0) {
+ g_warning ("Failed to read capture volume: %s", snd_strerror (ret));
+ continue;
+ }
+ data->channels++;
+
+ /* The single value volume is the highest channel volume */
+ if (data->volume < volume)
+ data->volume = volume;
+
+ data->c[channel] = alsa_channel_map_from[channel];
+ data->v[channel] = (guint) volume;
+ }
+ }
+}
diff --git a/backends/alsa/alsa-stream-input-control.h b/backends/alsa/alsa-stream-input-control.h
new file mode 100644
index 0000000..c427e3c
--- /dev/null
+++ b/backends/alsa/alsa-stream-input-control.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_STREAM_INPUT_CONTROL_H
+#define ALSA_STREAM_INPUT_CONTROL_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "alsa-stream-control.h"
+
+G_BEGIN_DECLS
+
+#define ALSA_TYPE_STREAM_INPUT_CONTROL \
+ (alsa_stream_input_control_get_type ())
+#define ALSA_STREAM_INPUT_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_STREAM_INPUT_CONTROL, AlsaStreamInputControl))
+#define ALSA_IS_STREAM_INPUT_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_STREAM_INPUT_CONTROL))
+#define ALSA_STREAM_INPUT_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_STREAM_INPUT_CONTROL, AlsaStreamInputControlClass))
+#define ALSA_IS_STREAM_INPUT_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_STREAM_INPUT_CONTROL))
+#define ALSA_STREAM_INPUT_CONTROL_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_STREAM_INPUT_CONTROL, AlsaStreamInputControlClass))
+
+typedef struct _AlsaStreamInputControl AlsaStreamInputControl;
+typedef struct _AlsaStreamInputControlClass AlsaStreamInputControlClass;
+typedef struct _AlsaStreamInputControlPrivate AlsaStreamInputControlPrivate;
+
+struct _AlsaStreamInputControl
+{
+ AlsaStreamControl parent;
+};
+
+struct _AlsaStreamInputControlClass
+{
+ AlsaStreamControlClass parent_class;
+};
+
+GType alsa_stream_input_control_get_type (void) G_GNUC_CONST;
+
+AlsaStreamControl *alsa_stream_input_control_new (const gchar *name,
+ const gchar *label,
+ MateMixerStreamControlRole role);
+
+G_END_DECLS
+
+#endif /* ALSA_STREAM_INPUT_CONTROL_H */
diff --git a/backends/alsa/alsa-stream-output-control.c b/backends/alsa/alsa-stream-output-control.c
new file mode 100644
index 0000000..5a3e6b3
--- /dev/null
+++ b/backends/alsa/alsa-stream-output-control.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "alsa-element.h"
+#include "alsa-stream-control.h"
+#include "alsa-stream-output-control.h"
+
+static void alsa_stream_output_control_class_init (AlsaStreamOutputControlClass *klass);
+static void alsa_stream_output_control_init (AlsaStreamOutputControl *control);
+
+G_DEFINE_TYPE (AlsaStreamOutputControl, alsa_stream_output_control, ALSA_TYPE_STREAM_CONTROL)
+
+static gboolean alsa_stream_output_control_load (AlsaStreamControl *control);
+
+static gboolean alsa_stream_output_control_set_mute (AlsaStreamControl *control,
+ gboolean mute);
+
+static gboolean alsa_stream_output_control_set_volume (AlsaStreamControl *control,
+ guint volume);
+
+static gboolean alsa_stream_output_control_set_channel_volume (AlsaStreamControl *control,
+ snd_mixer_selem_channel_id_t channel,
+ guint volume);
+
+static gboolean alsa_stream_output_control_get_volume_from_decibel (AlsaStreamControl *control,
+ gdouble decibel,
+ guint *volume);
+
+static gboolean alsa_stream_output_control_get_decibel_from_volume (AlsaStreamControl *control,
+ guint volume,
+ gdouble *decibel);
+
+static void read_volume_data (snd_mixer_elem_t *el,
+ AlsaControlData *data);
+
+static void
+alsa_stream_output_control_class_init (AlsaStreamOutputControlClass *klass)
+{
+ AlsaStreamControlClass *control_class;
+
+ control_class = ALSA_STREAM_CONTROL_CLASS (klass);
+
+ control_class->load = alsa_stream_output_control_load;
+ control_class->set_mute = alsa_stream_output_control_set_mute;
+ control_class->set_volume = alsa_stream_output_control_set_volume;
+ control_class->set_channel_volume = alsa_stream_output_control_set_channel_volume;
+ control_class->get_volume_from_decibel = alsa_stream_output_control_get_volume_from_decibel;
+ control_class->get_decibel_from_volume = alsa_stream_output_control_get_decibel_from_volume;
+}
+
+static void
+alsa_stream_output_control_init (AlsaStreamOutputControl *control)
+{
+}
+
+AlsaStreamControl *
+alsa_stream_output_control_new (const gchar *name,
+ const gchar *label,
+ MateMixerStreamControlRole role)
+{
+ return g_object_new (ALSA_TYPE_STREAM_OUTPUT_CONTROL,
+ "name", name,
+ "label", label,
+ "role", role,
+ NULL);
+}
+
+static gboolean
+alsa_stream_output_control_load (AlsaStreamControl *control)
+{
+ AlsaControlData data;
+ snd_mixer_elem_t *el;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_OUTPUT_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ /* Expect that the element has a volume control */
+ if G_UNLIKELY (snd_mixer_selem_has_playback_volume (el) == 0 &&
+ snd_mixer_selem_has_common_volume (el) == 0) {
+ g_warn_if_reached ();
+ return FALSE;
+ }
+
+ memset (&data, 0, sizeof (AlsaControlData));
+
+ /* We model any control switch as mute */
+ if (snd_mixer_selem_has_playback_switch (el) == 1 ||
+ snd_mixer_selem_has_common_switch (el) == 1)
+ data.switch_usable = TRUE;
+
+ data.active = snd_mixer_selem_is_active (el);
+
+ /* Read the volume data but do not error out if it fails, since ALSA reports
+ * the control to have a volume, expect the control to match what we need - slider
+ * with an optional mute toggle.
+ * If it fails to read the volume data, just treat it as a volumeless control */
+ read_volume_data (el, &data);
+
+ alsa_stream_control_set_data (control, &data);
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_output_control_set_mute (AlsaStreamControl *control, gboolean mute)
+{
+ snd_mixer_elem_t *el;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ /* Set the switch for all channels */
+ ret = snd_mixer_selem_set_playback_switch_all (el, !mute);
+ if (ret < 0) {
+ g_warning ("Failed to set playback switch: %s", snd_strerror (ret));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_output_control_set_volume (AlsaStreamControl *control, guint volume)
+{
+ snd_mixer_elem_t *el;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ /* Set the volume for all channels */
+ ret = snd_mixer_selem_set_playback_volume_all (el, volume);
+ if (ret < 0) {
+ g_warning ("Failed to set volume: %s", snd_strerror (ret));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_output_control_set_channel_volume (AlsaStreamControl *control,
+ snd_mixer_selem_channel_id_t channel,
+ guint volume)
+{
+ snd_mixer_elem_t *el;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ /* Set the volume for a single channels, the volume may still be "joined" and
+ * set all the channels by itself */
+ ret = snd_mixer_selem_set_playback_volume (el, channel, volume);
+ if (ret < 0) {
+ g_warning ("Failed to set channel volume: %s", snd_strerror (ret));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_output_control_get_volume_from_decibel (AlsaStreamControl *control,
+ gdouble decibel,
+ guint *volume)
+{
+ snd_mixer_elem_t *el;
+ glong value;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ ret = snd_mixer_selem_ask_playback_dB_vol (el, (glong) (decibel * 100), 0, &value);
+ if (ret < 0) {
+ g_warning ("Failed to convert volume: %s", snd_strerror (ret));
+ return FALSE;
+ }
+
+ *volume = value;
+ return TRUE;
+}
+
+static gboolean
+alsa_stream_output_control_get_decibel_from_volume (AlsaStreamControl *control,
+ guint volume,
+ gdouble *decibel)
+{
+ snd_mixer_elem_t *el;
+ glong value;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE);
+
+ el = alsa_element_get_snd_element (ALSA_ELEMENT (control));
+ if G_UNLIKELY (el == NULL)
+ return FALSE;
+
+ ret = snd_mixer_selem_ask_playback_vol_dB (el, (glong) volume, &value);
+ if (ret < 0) {
+ g_warning ("Failed to convert volume: %s", snd_strerror (ret));
+ return FALSE;
+ }
+
+ *decibel = value / 100.0;
+ return TRUE;
+}
+
+static void
+read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data)
+{
+ glong volume;
+ glong min, max;
+ gint ret;
+ gint i;
+
+ /* Read volume ranges, this call should never fail on valid input */
+ ret = snd_mixer_selem_get_playback_volume_range (el, &min, &max);
+ if G_UNLIKELY (ret < 0) {
+ g_warning ("Failed to read playback volume range: %s", snd_strerror (ret));
+ return;
+ }
+ data->min = (guint) min;
+ data->max = (guint) max;
+
+ /* This fails when decibels are not supported */
+ ret = snd_mixer_selem_get_playback_dB_range (el, &min, &max);
+ if (ret == 0) {
+ data->min_decibel = min / 100.0;
+ data->max_decibel = max / 100.0;
+ } else
+ data->min_decibel = data->max_decibel = -MATE_MIXER_INFINITY;
+
+ for (i = 0; i < MATE_MIXER_CHANNEL_MAX; i++)
+ data->v[i] = data->min;
+
+ data->volume = data->min;
+ data->volume_joined = snd_mixer_selem_has_playback_volume_joined (el);
+
+ if (data->switch_usable == TRUE)
+ data->switch_joined = snd_mixer_selem_has_playback_switch_joined (el);
+
+ if (snd_mixer_selem_is_playback_mono (el) == 1) {
+ /* Special handling for single channel controls */
+ ret = snd_mixer_selem_get_playback_volume (el, SND_MIXER_SCHN_MONO, &volume);
+ if (ret == 0) {
+ data->channels = 1;
+
+ data->c[0] = MATE_MIXER_CHANNEL_MONO;
+ data->v[0] = data->volume = (guint) volume;
+ } else {
+ g_warning ("Failed to read playback volume: %s", snd_strerror (ret));
+ }
+
+ if (data->switch_usable == TRUE) {
+ gint value;
+
+ ret = snd_mixer_selem_get_playback_switch (el, SND_MIXER_SCHN_MONO, &value);
+ if G_LIKELY (ret == 0)
+ data->m[0] = !value;
+ }
+ } else {
+ snd_mixer_selem_channel_id_t channel;
+
+ /* We use numeric channel indices, but ALSA only works with channel
+ * positions, go over all the positions supported by ALSA and create
+ * a list of channels */
+ for (channel = 0; channel < SND_MIXER_SCHN_LAST; channel++) {
+ if (snd_mixer_selem_has_playback_channel (el, channel) == 0)
+ continue;
+
+ if (data->switch_usable == TRUE) {
+ gint value;
+
+ ret = snd_mixer_selem_get_playback_switch (el, channel, &value);
+ if (ret == 0)
+ data->m[channel] = !value;
+ }
+
+ ret = snd_mixer_selem_get_playback_volume (el, channel, &volume);
+ if (ret < 0) {
+ g_warning ("Failed to read playback volume: %s", snd_strerror (ret));
+ continue;
+ }
+ data->channels++;
+
+ /* The single value volume is the highest channel volume */
+ if (data->volume < volume)
+ data->volume = volume;
+
+ data->c[channel] = alsa_channel_map_from[channel];
+ data->v[channel] = (guint) volume;
+ }
+ }
+}
diff --git a/backends/alsa/alsa-stream-output-control.h b/backends/alsa/alsa-stream-output-control.h
new file mode 100644
index 0000000..845eaae
--- /dev/null
+++ b/backends/alsa/alsa-stream-output-control.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_STREAM_OUTPUT_CONTROL_H
+#define ALSA_STREAM_OUTPUT_CONTROL_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "alsa-stream-control.h"
+
+G_BEGIN_DECLS
+
+#define ALSA_TYPE_STREAM_OUTPUT_CONTROL \
+ (alsa_stream_output_control_get_type ())
+#define ALSA_STREAM_OUTPUT_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_STREAM_OUTPUT_CONTROL, AlsaStreamOutputControl))
+#define ALSA_IS_STREAM_OUTPUT_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_STREAM_OUTPUT_CONTROL))
+#define ALSA_STREAM_OUTPUT_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_STREAM_OUTPUT_CONTROL, AlsaStreamOutputControlClass))
+#define ALSA_IS_STREAM_OUTPUT_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_STREAM_OUTPUT_CONTROL))
+#define ALSA_STREAM_OUTPUT_CONTROL_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_STREAM_OUTPUT_CONTROL, AlsaStreamOutputControlClass))
+
+typedef struct _AlsaStreamOutputControl AlsaStreamOutputControl;
+typedef struct _AlsaStreamOutputControlClass AlsaStreamOutputControlClass;
+typedef struct _AlsaStreamOutputControlPrivate AlsaStreamOutputControlPrivate;
+
+struct _AlsaStreamOutputControl
+{
+ AlsaStreamControl parent;
+};
+
+struct _AlsaStreamOutputControlClass
+{
+ AlsaStreamControlClass parent_class;
+};
+
+GType alsa_stream_output_control_get_type (void) G_GNUC_CONST;
+
+AlsaStreamControl *alsa_stream_output_control_new (const gchar *name,
+ const gchar *label,
+ MateMixerStreamControlRole role);
+
+G_END_DECLS
+
+#endif /* ALSA_STREAM_OUTPUT_CONTROL_H */
diff --git a/backends/alsa/alsa-stream.c b/backends/alsa/alsa-stream.c
new file mode 100644
index 0000000..d2f68d4
--- /dev/null
+++ b/backends/alsa/alsa-stream.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "alsa-element.h"
+#include "alsa-stream.h"
+#include "alsa-stream-control.h"
+#include "alsa-switch.h"
+
+struct _AlsaStreamPrivate
+{
+ GHashTable *switches;
+ GHashTable *controls;
+ MateMixerStreamControl *control;
+};
+
+static void alsa_stream_class_init (AlsaStreamClass *klass);
+static void alsa_stream_init (AlsaStream *stream);
+static void alsa_stream_dispose (GObject *object);
+static void alsa_stream_finalize (GObject *object);
+
+G_DEFINE_TYPE (AlsaStream, alsa_stream, MATE_MIXER_TYPE_STREAM)
+
+static MateMixerStreamControl *alsa_stream_get_control (MateMixerStream *mms,
+ const gchar *name);
+static MateMixerStreamControl *alsa_stream_get_default_control (MateMixerStream *mms);
+
+static MateMixerSwitch * alsa_stream_get_switch (MateMixerStream *mms,
+ const gchar *name);
+
+static GList * alsa_stream_list_controls (MateMixerStream *mms);
+static GList * alsa_stream_list_switches (MateMixerStream *mms);
+
+static void
+alsa_stream_class_init (AlsaStreamClass *klass)
+{
+ GObjectClass *object_class;
+ MateMixerStreamClass *stream_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = alsa_stream_dispose;
+ object_class->finalize = alsa_stream_finalize;
+
+ stream_class = MATE_MIXER_STREAM_CLASS (klass);
+ stream_class->get_control = alsa_stream_get_control;
+ stream_class->get_default_control = alsa_stream_get_default_control;
+ stream_class->get_switch = alsa_stream_get_switch;
+ stream_class->list_controls = alsa_stream_list_controls;
+ stream_class->list_switches = alsa_stream_list_switches;
+
+ g_type_class_add_private (object_class, sizeof (AlsaStreamPrivate));
+}
+
+static void
+alsa_stream_init (AlsaStream *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+ ALSA_TYPE_STREAM,
+ AlsaStreamPrivate);
+
+ stream->priv->controls = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ stream->priv->switches = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+}
+
+static void
+alsa_stream_dispose (GObject *object)
+{
+ AlsaStream *stream;
+
+ stream = ALSA_STREAM (object);
+
+ g_hash_table_remove_all (stream->priv->controls);
+ g_hash_table_remove_all (stream->priv->switches);
+
+ g_clear_object (&stream->priv->control);
+
+ G_OBJECT_CLASS (alsa_stream_parent_class)->dispose (object);
+}
+
+static void
+alsa_stream_finalize (GObject *object)
+{
+ AlsaStream *stream;
+
+ stream = ALSA_STREAM (object);
+
+ g_hash_table_destroy (stream->priv->controls);
+ g_hash_table_destroy (stream->priv->switches);
+
+ G_OBJECT_CLASS (alsa_stream_parent_class)->finalize (object);
+}
+
+AlsaStream *
+alsa_stream_new (const gchar *name,
+ MateMixerDevice *device,
+ MateMixerStreamFlags flags)
+{
+ return g_object_new (ALSA_TYPE_STREAM,
+ "name", name,
+ "device", device,
+ "flags", flags,
+ NULL);
+}
+
+void
+alsa_stream_add_control (AlsaStream *stream, AlsaStreamControl *control)
+{
+ const gchar *name;
+
+ g_return_if_fail (ALSA_IS_STREAM (stream));
+ g_return_if_fail (ALSA_IS_STREAM_CONTROL (control));
+
+ name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control));
+ g_hash_table_insert (stream->priv->controls,
+ g_strdup (name),
+ g_object_ref (control));
+}
+
+void
+alsa_stream_add_switch (AlsaStream *stream, AlsaSwitch *swtch)
+{
+ const gchar *name;
+
+ g_return_if_fail (ALSA_IS_STREAM (stream));
+ g_return_if_fail (ALSA_IS_SWITCH (swtch));
+
+ name = mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch));
+ g_hash_table_insert (stream->priv->switches,
+ g_strdup (name),
+ g_object_ref (swtch));
+}
+
+gboolean
+alsa_stream_is_empty (AlsaStream *stream)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
+
+ if (g_hash_table_size (stream->priv->controls) > 0 ||
+ g_hash_table_size (stream->priv->switches) > 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+void
+alsa_stream_set_default_control (AlsaStream *stream, AlsaStreamControl *control)
+{
+ g_return_if_fail (ALSA_IS_STREAM (stream));
+ g_return_if_fail (ALSA_IS_STREAM_CONTROL (control));
+
+ /* This function is only used internally so avoid validating that the control
+ * belongs to this stream */
+ if (stream->priv->control != NULL)
+ g_object_unref (stream->priv->control);
+
+ if (control != NULL)
+ stream->priv->control = MATE_MIXER_STREAM_CONTROL (g_object_ref (control));
+ else
+ stream->priv->control = NULL;
+}
+
+void
+alsa_stream_load_elements (AlsaStream *stream, const gchar *name)
+{
+ AlsaElement *element;
+
+ g_return_if_fail (ALSA_IS_STREAM (stream));
+ g_return_if_fail (name != NULL);
+
+ element = g_hash_table_lookup (stream->priv->controls, name);
+ if (element != NULL)
+ alsa_element_load (element);
+
+ element = g_hash_table_lookup (stream->priv->switches, name);
+ if (element != NULL)
+ alsa_element_load (element);
+}
+
+gboolean
+alsa_stream_remove_elements (AlsaStream *stream, const gchar *name)
+{
+ gboolean removed = FALSE;
+
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (g_hash_table_remove (stream->priv->controls, name) == TRUE)
+ removed = TRUE;
+ if (g_hash_table_remove (stream->priv->switches, name) == TRUE)
+ removed = TRUE;
+
+ return removed;
+}
+
+static MateMixerStreamControl *
+alsa_stream_get_control (MateMixerStream *mms, const gchar *name)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+
+ return g_hash_table_lookup (ALSA_STREAM (mms)->priv->controls, name);
+}
+
+static MateMixerStreamControl *
+alsa_stream_get_default_control (MateMixerStream *mms)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+
+ return ALSA_STREAM (mms)->priv->control;
+}
+
+static MateMixerSwitch *
+alsa_stream_get_switch (MateMixerStream *mms, const gchar *name)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+
+ return g_hash_table_lookup (ALSA_STREAM (mms)->priv->switches, name);
+}
+
+static GList *
+alsa_stream_list_controls (MateMixerStream *mms)
+{
+ GList *list;
+
+ g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+
+ /* 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 (ALSA_STREAM (mms)->priv->controls);
+ if (list != NULL)
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+ return list;
+}
+
+static GList *
+alsa_stream_list_switches (MateMixerStream *mms)
+{
+ GList *list;
+
+ g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+
+ /* 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 (ALSA_STREAM (mms)->priv->switches);
+ if (list != NULL)
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+
+ return list;
+}
diff --git a/backends/alsa/alsa-stream.h b/backends/alsa/alsa-stream.h
new file mode 100644
index 0000000..f26a643
--- /dev/null
+++ b/backends/alsa/alsa-stream.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_STREAM_H
+#define ALSA_STREAM_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "alsa-element.h"
+#include "alsa-stream-control.h"
+#include "alsa-switch.h"
+
+G_BEGIN_DECLS
+
+#define ALSA_TYPE_STREAM \
+ (alsa_stream_get_type ())
+#define ALSA_STREAM(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_STREAM, AlsaStream))
+#define ALSA_IS_STREAM(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_STREAM))
+#define ALSA_STREAM_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_STREAM, AlsaStreamClass))
+#define ALSA_IS_STREAM_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_STREAM))
+#define ALSA_STREAM_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_STREAM, AlsaStreamClass))
+
+typedef struct _AlsaStream AlsaStream;
+typedef struct _AlsaStreamClass AlsaStreamClass;
+typedef struct _AlsaStreamPrivate AlsaStreamPrivate;
+
+struct _AlsaStream
+{
+ MateMixerStream parent;
+
+ /*< private >*/
+ AlsaStreamPrivate *priv;
+};
+
+struct _AlsaStreamClass
+{
+ MateMixerStreamClass parent_class;
+};
+
+GType alsa_stream_get_type (void) G_GNUC_CONST;
+
+AlsaStream *alsa_stream_new (const gchar *name,
+ MateMixerDevice *device,
+ MateMixerStreamFlags flags);
+
+void alsa_stream_add_control (AlsaStream *stream,
+ AlsaStreamControl *control);
+
+void alsa_stream_add_switch (AlsaStream *stream,
+ AlsaSwitch *swtch);
+
+gboolean alsa_stream_is_empty (AlsaStream *stream);
+
+void alsa_stream_set_default_control (AlsaStream *stream,
+ AlsaStreamControl *control);
+
+void alsa_stream_load_elements (AlsaStream *stream,
+ const gchar *name);
+
+gboolean alsa_stream_remove_elements (AlsaStream *stream,
+ const gchar *name);
+
+void alsa_stream_remove_all (AlsaStream *stream);
+
+G_END_DECLS
+
+#endif /* ALSA_STREAM_H */
diff --git a/backends/alsa/alsa-switch-option.c b/backends/alsa/alsa-switch-option.c
new file mode 100644
index 0000000..2173113
--- /dev/null
+++ b/backends/alsa/alsa-switch-option.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "alsa-switch-option.h"
+
+struct _AlsaSwitchOptionPrivate
+{
+ guint id;
+};
+
+static void alsa_switch_option_class_init (AlsaSwitchOptionClass *klass);
+static void alsa_switch_option_init (AlsaSwitchOption *option);
+
+G_DEFINE_TYPE (AlsaSwitchOption, alsa_switch_option, MATE_MIXER_TYPE_SWITCH_OPTION)
+
+static void
+alsa_switch_option_class_init (AlsaSwitchOptionClass *klass)
+{
+ g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (AlsaSwitchOptionPrivate));
+}
+
+static void
+alsa_switch_option_init (AlsaSwitchOption *option)
+{
+ option->priv = G_TYPE_INSTANCE_GET_PRIVATE (option,
+ ALSA_TYPE_SWITCH_OPTION,
+ AlsaSwitchOptionPrivate);
+}
+
+AlsaSwitchOption *
+alsa_switch_option_new (const gchar *name,
+ const gchar *label,
+ const gchar *icon,
+ guint id)
+{
+ AlsaSwitchOption *option;
+
+ option = g_object_new (ALSA_TYPE_SWITCH_OPTION,
+ "name", name,
+ "label", label,
+ NULL);
+
+ option->priv->id = id;
+ return option;
+}
+
+guint
+alsa_switch_option_get_id (AlsaSwitchOption *option)
+{
+ g_return_val_if_fail (ALSA_IS_SWITCH_OPTION (option), 0);
+
+ return option->priv->id;
+}
diff --git a/backends/alsa/alsa-switch-option.h b/backends/alsa/alsa-switch-option.h
new file mode 100644
index 0000000..c2dda87
--- /dev/null
+++ b/backends/alsa/alsa-switch-option.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_SWITCH_OPTION_H
+#define ALSA_SWITCH_OPTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+G_BEGIN_DECLS
+
+#define ALSA_TYPE_SWITCH_OPTION \
+ (alsa_switch_option_get_type ())
+#define ALSA_SWITCH_OPTION(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOption))
+#define ALSA_IS_SWITCH_OPTION(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_SWITCH_OPTION))
+#define ALSA_SWITCH_OPTION_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass))
+#define ALSA_IS_SWITCH_OPTION_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_SWITCH_OPTION))
+#define ALSA_SWITCH_OPTION_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass))
+
+typedef struct _AlsaSwitchOption AlsaSwitchOption;
+typedef struct _AlsaSwitchOptionClass AlsaSwitchOptionClass;
+typedef struct _AlsaSwitchOptionPrivate AlsaSwitchOptionPrivate;
+
+struct _AlsaSwitchOption
+{
+ MateMixerSwitchOption parent;
+
+ /*< private >*/
+ AlsaSwitchOptionPrivate *priv;
+};
+
+struct _AlsaSwitchOptionClass
+{
+ MateMixerSwitchOptionClass parent_class;
+};
+
+GType alsa_switch_option_get_type (void) G_GNUC_CONST;
+
+AlsaSwitchOption *alsa_switch_option_new (const gchar *name,
+ const gchar *label,
+ const gchar *icon,
+ guint id);
+
+guint alsa_switch_option_get_id (AlsaSwitchOption *option);
+
+G_END_DECLS
+
+#endif /* ALSA_SWITCH_OPTION_H */
diff --git a/backends/alsa/alsa-switch.c b/backends/alsa/alsa-switch.c
new file mode 100644
index 0000000..15151ae
--- /dev/null
+++ b/backends/alsa/alsa-switch.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "alsa-element.h"
+#include "alsa-switch.h"
+#include "alsa-switch-option.h"
+
+struct _AlsaSwitchPrivate
+{
+ GList *options;
+ guint32 channel_mask;
+ snd_mixer_elem_t *element;
+};
+
+static void alsa_element_interface_init (AlsaElementInterface *iface);
+
+static void alsa_switch_class_init (AlsaSwitchClass *klass);
+static void alsa_switch_init (AlsaSwitch *swtch);
+
+G_DEFINE_TYPE_WITH_CODE (AlsaSwitch, alsa_switch,
+ MATE_MIXER_TYPE_SWITCH,
+ G_IMPLEMENT_INTERFACE (ALSA_TYPE_ELEMENT,
+ alsa_element_interface_init))
+
+static gboolean alsa_switch_set_active_option (MateMixerSwitch *mms,
+ MateMixerSwitchOption *mmso);
+
+static GList * alsa_switch_list_options (MateMixerSwitch *mms);
+
+static snd_mixer_elem_t * alsa_switch_get_snd_element (AlsaElement *element);
+static void alsa_switch_set_snd_element (AlsaElement *element,
+ snd_mixer_elem_t *el);
+static gboolean alsa_switch_load (AlsaElement *element);
+
+static void
+alsa_element_interface_init (AlsaElementInterface *iface)
+{
+ iface->get_snd_element = alsa_switch_get_snd_element;
+ iface->set_snd_element = alsa_switch_set_snd_element;
+ iface->load = alsa_switch_load;
+}
+
+static void
+alsa_switch_class_init (AlsaSwitchClass *klass)
+{
+ MateMixerSwitchClass *switch_class;
+
+ switch_class = MATE_MIXER_SWITCH_CLASS (klass);
+ switch_class->set_active_option = alsa_switch_set_active_option;
+ switch_class->list_options = alsa_switch_list_options;
+
+ g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (AlsaSwitchPrivate));
+}
+
+static void
+alsa_switch_init (AlsaSwitch *swtch)
+{
+ swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch,
+ ALSA_TYPE_SWITCH,
+ AlsaSwitchPrivate);
+}
+
+AlsaSwitch *
+alsa_switch_new (const gchar *name, const gchar *label, GList *options)
+{
+ AlsaSwitch *swtch;
+
+ swtch = g_object_new (ALSA_TYPE_SWITCH,
+ "name", name,
+ "label", label,
+ NULL);
+
+ /* Takes ownership of options */
+ swtch->priv->options = options;
+ return swtch;
+}
+
+static gboolean
+alsa_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso)
+{
+ AlsaSwitch *swtch;
+ guint index;
+ gboolean set_item = FALSE;
+ snd_mixer_selem_channel_id_t channel;
+
+ g_return_val_if_fail (ALSA_IS_SWITCH (mms), FALSE);
+ g_return_val_if_fail (ALSA_IS_SWITCH_OPTION (mmso), FALSE);
+
+ swtch = ALSA_SWITCH (mms);
+
+ /* The channel mask is created when reading the active option the first
+ * time, so a successful load must be done before changing the option */
+ if G_UNLIKELY (swtch->priv->channel_mask == 0) {
+ g_debug ("Not setting active switch option, channel mask unknown");
+ return FALSE;
+ }
+
+ index = alsa_switch_option_get_id (ALSA_SWITCH_OPTION (mmso));
+
+ for (channel = 0; channel < SND_MIXER_SCHN_LAST; channel++) {
+ /* The option is set per-channel, make sure to set it only for channels
+ * we successfully read the value from */
+ if (swtch->priv->channel_mask & (1 << channel)) {
+ gint ret = snd_mixer_selem_set_enum_item (swtch->priv->element,
+ channel,
+ index);
+ if (ret == 0)
+ set_item = TRUE;
+ else
+ g_warning ("Failed to set active option of switch %s: %s",
+ snd_mixer_selem_get_name (swtch->priv->element),
+ snd_strerror (ret));
+ }
+ }
+ return set_item;
+}
+
+static GList *
+alsa_switch_list_options (MateMixerSwitch *swtch)
+{
+ g_return_val_if_fail (ALSA_IS_SWITCH (swtch), NULL);
+
+ return ALSA_SWITCH (swtch)->priv->options;
+}
+
+static snd_mixer_elem_t *
+alsa_switch_get_snd_element (AlsaElement *element)
+{
+ g_return_val_if_fail (ALSA_IS_SWITCH (element), NULL);
+
+ return ALSA_SWITCH (element)->priv->element;
+}
+
+static void
+alsa_switch_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
+{
+ g_return_if_fail (ALSA_IS_SWITCH (element));
+ g_return_if_fail (el != NULL);
+
+ ALSA_SWITCH (element)->priv->element = el;
+}
+
+static gboolean
+alsa_switch_load (AlsaElement *element)
+{
+ AlsaSwitch *swtch;
+ GList *list;
+ guint item;
+ gint ret;
+ snd_mixer_selem_channel_id_t c;
+
+ swtch = ALSA_SWITCH (element);
+
+ /* When reading the first time we try all the channels, otherwise only the
+ * ones which returned success before */
+ if (swtch->priv->channel_mask == 0) {
+ for (c = 0; c < SND_MIXER_SCHN_LAST; c++) {
+ ret = snd_mixer_selem_get_enum_item (swtch->priv->element, c, &item);
+
+ /* The active enum option is set per-channel, so when reading it the
+ * first time, create a mask of all channels for which we read the
+ * value successfully */
+ if (ret == 0)
+ swtch->priv->channel_mask |= 1 << c;
+ }
+
+ /* The last ALSA call might have failed, but it doesn't matter if we have
+ * a channel mask */
+ if (swtch->priv->channel_mask > 0)
+ ret = 0;
+ } else {
+ for (c = 0; !(swtch->priv->channel_mask & (1 << c)); c++)
+ ;
+
+ /* When not reading the mask, the first usable channel is enough, we don't
+ * support per-channel selections anyway */
+ ret = snd_mixer_selem_get_enum_item (swtch->priv->element, c, &item);
+ }
+
+ if (ret < 0) {
+ g_warning ("Failed to read active option of switch %s: %s",
+ snd_mixer_selem_get_name (swtch->priv->element),
+ snd_strerror (ret));
+ return FALSE;
+ }
+
+ list = swtch->priv->options;
+ while (list != NULL) {
+ AlsaSwitchOption *option = ALSA_SWITCH_OPTION (list->data);
+
+ /* Mark the selected option when we find it, ALSA indentifies them
+ * by numeric indices */
+ if (alsa_switch_option_get_id (option) == item) {
+ _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch),
+ MATE_MIXER_SWITCH_OPTION (option));
+ return TRUE;
+ }
+ list = list->next;
+ }
+
+ g_warning ("Unknown active option of switch %s: %d",
+ snd_mixer_selem_get_name (swtch->priv->element),
+ item);
+
+ return FALSE;
+}
diff --git a/backends/alsa/alsa-switch.h b/backends/alsa/alsa-switch.h
new file mode 100644
index 0000000..fdcfb87
--- /dev/null
+++ b/backends/alsa/alsa-switch.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_SWITCH_H
+#define ALSA_SWITCH_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+G_BEGIN_DECLS
+
+#define ALSA_TYPE_SWITCH \
+ (alsa_switch_get_type ())
+#define ALSA_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_SWITCH, AlsaSwitch))
+#define ALSA_IS_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_SWITCH))
+#define ALSA_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_SWITCH, AlsaSwitchClass))
+#define ALSA_IS_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_SWITCH))
+#define ALSA_SWITCH_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_SWITCH, AlsaSwitchClass))
+
+typedef struct _AlsaSwitch AlsaSwitch;
+typedef struct _AlsaSwitchClass AlsaSwitchClass;
+typedef struct _AlsaSwitchPrivate AlsaSwitchPrivate;
+
+struct _AlsaSwitch
+{
+ MateMixerSwitch parent;
+
+ /*< private >*/
+ AlsaSwitchPrivate *priv;
+};
+
+struct _AlsaSwitchClass
+{
+ MateMixerSwitchClass parent_class;
+};
+
+GType alsa_switch_get_type (void) G_GNUC_CONST;
+
+AlsaSwitch *alsa_switch_new (const gchar *name,
+ const gchar *label,
+ GList *options);
+
+G_END_DECLS
+
+#endif /* ALSA_SWITCH_H */
diff --git a/backends/alsa/alsa-toggle.c b/backends/alsa/alsa-toggle.c
new file mode 100644
index 0000000..efa3460
--- /dev/null
+++ b/backends/alsa/alsa-toggle.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "alsa-element.h"
+#include "alsa-switch-option.h"
+#include "alsa-toggle.h"
+
+struct _AlsaTogglePrivate
+{
+ AlsaToggleType type;
+ guint32 channel_mask;
+ snd_mixer_elem_t *element;
+};
+
+static void alsa_element_interface_init (AlsaElementInterface *iface);
+
+static void alsa_toggle_class_init (AlsaToggleClass *klass);
+static void alsa_toggle_init (AlsaToggle *toggle);
+
+G_DEFINE_TYPE_WITH_CODE (AlsaToggle, alsa_toggle, MATE_MIXER_TYPE_TOGGLE,
+ G_IMPLEMENT_INTERFACE (ALSA_TYPE_ELEMENT,
+ alsa_element_interface_init))
+
+static gboolean alsa_toggle_set_active_option (MateMixerSwitch *mms,
+ MateMixerSwitchOption *mmso);
+
+static snd_mixer_elem_t * alsa_toggle_get_snd_element (AlsaElement *element);
+static void alsa_toggle_set_snd_element (AlsaElement *element,
+ snd_mixer_elem_t *el);
+static gboolean alsa_toggle_load (AlsaElement *element);
+
+static void
+alsa_element_interface_init (AlsaElementInterface *iface)
+{
+ iface->get_snd_element = alsa_toggle_get_snd_element;
+ iface->set_snd_element = alsa_toggle_set_snd_element;
+ iface->load = alsa_toggle_load;
+}
+
+static void
+alsa_toggle_class_init (AlsaToggleClass *klass)
+{
+ MateMixerSwitchClass *switch_class;
+
+ switch_class = MATE_MIXER_SWITCH_CLASS (klass);
+ switch_class->set_active_option = alsa_toggle_set_active_option;
+
+ g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (AlsaTogglePrivate));
+}
+
+static void
+alsa_toggle_init (AlsaToggle *toggle)
+{
+ toggle->priv = G_TYPE_INSTANCE_GET_PRIVATE (toggle,
+ ALSA_TYPE_TOGGLE,
+ AlsaTogglePrivate);
+}
+
+AlsaToggle *
+alsa_toggle_new (const gchar *name,
+ const gchar *label,
+ AlsaToggleType type,
+ AlsaSwitchOption *on,
+ AlsaSwitchOption *off)
+{
+ AlsaToggle *toggle;
+
+ toggle = g_object_new (ALSA_TYPE_TOGGLE,
+ "name", name,
+ "label", label,
+ "state-option-on", on,
+ "state-option-off", off,
+ NULL);
+
+ toggle->priv->type = type;
+ return toggle;
+}
+
+static gboolean
+alsa_toggle_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso)
+{
+ AlsaToggle *toggle;
+ gint value;
+ gint ret;
+
+ g_return_val_if_fail (ALSA_IS_TOGGLE (mms), FALSE);
+ g_return_val_if_fail (ALSA_IS_SWITCH_OPTION (mmso), FALSE);
+
+ toggle = ALSA_TOGGLE (mms);
+
+ /* For toggles the 0/1 value is stored as the switch option id */
+ value = alsa_switch_option_get_id (ALSA_SWITCH_OPTION (mmso));
+ if G_UNLIKELY (value != 0 && value != 1) {
+ g_warn_if_reached ();
+ return FALSE;
+ }
+
+ if (toggle->priv->type == ALSA_TOGGLE_CAPTURE)
+ ret = snd_mixer_selem_set_capture_switch_all (toggle->priv->element, value);
+ else
+ ret = snd_mixer_selem_set_playback_switch_all (toggle->priv->element, value);
+
+ if (ret < 0) {
+ g_warning ("Failed to set value of toggle %s: %s",
+ snd_mixer_selem_get_name (toggle->priv->element),
+ snd_strerror (ret));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static snd_mixer_elem_t *
+alsa_toggle_get_snd_element (AlsaElement *element)
+{
+ g_return_val_if_fail (ALSA_IS_TOGGLE (element), NULL);
+
+ return ALSA_TOGGLE (element)->priv->element;
+}
+
+static void
+alsa_toggle_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
+{
+ g_return_if_fail (ALSA_IS_TOGGLE (element));
+ g_return_if_fail (el != NULL);
+
+ ALSA_TOGGLE (element)->priv->element = el;
+}
+
+static gboolean
+alsa_toggle_load (AlsaElement *element)
+{
+ AlsaToggle *toggle;
+ gint value;
+ gint ret;
+ snd_mixer_selem_channel_id_t c;
+
+ toggle = ALSA_TOGGLE (element);
+
+ /* When reading the first time we try all the channels, otherwise only the
+ * ones which returned success before */
+ if (toggle->priv->channel_mask == 0) {
+ for (c = 0; c < SND_MIXER_SCHN_LAST; c++) {
+ if (toggle->priv->type == ALSA_TOGGLE_CAPTURE)
+ ret = snd_mixer_selem_get_capture_switch (toggle->priv->element,
+ c,
+ &value);
+ else
+ ret = snd_mixer_selem_get_playback_switch (toggle->priv->element,
+ c,
+ &value);
+
+ /* The active enum option is set per-channel, so when reading it the
+ * first time, create a mask of all channels for which we read the
+ * value successfully */
+ if (ret == 0)
+ toggle->priv->channel_mask |= 1 << c;
+ }
+
+ /* The last ALSA call might have failed, but it doesn't matter if we have
+ * a channel mask */
+ if (toggle->priv->channel_mask > 0)
+ ret = 0;
+ } else {
+ for (c = 0; !(toggle->priv->channel_mask & (1 << c)); c++)
+ ;
+
+ /* When not reading the mask, the first usable channel is enough, we don't
+ * support per-channel selections anyway */
+ if (toggle->priv->type == ALSA_TOGGLE_CAPTURE)
+ ret = snd_mixer_selem_get_capture_switch (toggle->priv->element,
+ c,
+ &value);
+ else
+ ret = snd_mixer_selem_get_playback_switch (toggle->priv->element,
+ c,
+ &value);
+ }
+
+ if (ret == 0) {
+ MateMixerSwitchOption *active;
+
+ if (value > 0)
+ active = mate_mixer_toggle_get_state_option (MATE_MIXER_TOGGLE (toggle), TRUE);
+ else
+ active = mate_mixer_toggle_get_state_option (MATE_MIXER_TOGGLE (toggle), FALSE);
+
+ _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (toggle), active);
+
+ return TRUE;
+ }
+
+ g_warning ("Failed to read state of toggle %s: %s",
+ snd_mixer_selem_get_name (toggle->priv->element),
+ snd_strerror (ret));
+
+ return FALSE;
+}
diff --git a/backends/alsa/alsa-toggle.h b/backends/alsa/alsa-toggle.h
new file mode 100644
index 0000000..d9c083b
--- /dev/null
+++ b/backends/alsa/alsa-toggle.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALSA_TOGGLE_H
+#define ALSA_TOGGLE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "alsa-switch-option.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ ALSA_TOGGLE_CAPTURE,
+ ALSA_TOGGLE_PLAYBACK
+} AlsaToggleType;
+
+#define ALSA_TYPE_TOGGLE \
+ (alsa_toggle_get_type ())
+#define ALSA_TOGGLE(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_TOGGLE, AlsaToggle))
+#define ALSA_IS_TOGGLE(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_TOGGLE))
+#define ALSA_TOGGLE_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_TOGGLE, AlsaToggleClass))
+#define ALSA_IS_TOGGLE_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_TOGGLE))
+#define ALSA_TOGGLE_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_TOGGLE, AlsaToggleClass))
+
+typedef struct _AlsaToggle AlsaToggle;
+typedef struct _AlsaToggleClass AlsaToggleClass;
+typedef struct _AlsaTogglePrivate AlsaTogglePrivate;
+
+struct _AlsaToggle
+{
+ MateMixerToggle parent;
+
+ /*< private >*/
+ AlsaTogglePrivate *priv;
+};
+
+struct _AlsaToggleClass
+{
+ MateMixerToggleClass parent_class;
+};
+
+GType alsa_toggle_get_type (void) G_GNUC_CONST;
+
+AlsaToggle *alsa_toggle_new (const gchar *name,
+ const gchar *label,
+ AlsaToggleType type,
+ AlsaSwitchOption *on,
+ AlsaSwitchOption *off);
+
+G_END_DECLS
+
+#endif /* ALSA_TOGGLE_H */
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 <string.h>
#include <errno.h>
#include <glib.h>
-#include <glib-object.h>
#include <glib/gstdio.h>
-#include <glib/gprintf.h>
#include <glib/gi18n.h>
-#include <gio/gio.h>
+#include <glib-object.h>
-#include <libmatemixer/matemixer-backend.h>
-#include <libmatemixer/matemixer-backend-module.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#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;
@@ -124,65 +111,31 @@ const MateMixerBackendInfo *backend_module_get_info (void)
}
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)
{
oss->priv = G_TYPE_INSTANCE_GET_PRIVATE (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: <ATI R6xx (HDMI)> (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: <ATI R6xx (HDMI)> (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 <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer-backend.h>
#include <libmatemixer/matemixer-backend-module.h>
#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 <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
+#include <unistd.h>
#ifdef HAVE_SYS_SOUNDCARD_H
# include <sys/soundcard.h>
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 <glib/gstdio.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-device.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-port.h>
-#include <libmatemixer/matemixer-port-private.h>
-#include <libmatemixer/matemixer-stream.h>
-#include <libmatemixer/matemixer-stream-control.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#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 <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#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 <http://www.gnu.org/licenses/>.
*/
-#include <unistd.h>
#include <errno.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-port.h>
-#include <libmatemixer/matemixer-stream-control.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#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 <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
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 <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
#include <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
-#include <libmatemixer/matemixer-device.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-stream.h>
-
+#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 <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+#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
diff --git a/backends/oss4/Makefile.am b/backends/oss4/Makefile.am
deleted file mode 100644
index cca8723..0000000
--- a/backends/oss4/Makefile.am
+++ /dev/null
@@ -1,29 +0,0 @@
-backenddir = $(libdir)/libmatemixer
-
-backend_LTLIBRARIES = libmatemixer-oss4.la
-
-AM_CPPFLAGS = \
- -I$(top_srcdir) \
- -DG_LOG_DOMAIN=\"libmatemixer-oss4\"
-
-libmatemixer_oss4_la_CFLAGS = \
- $(GLIB_CFLAGS) \
- $(OSS4_CFLAGS)
-
-libmatemixer_oss4_la_SOURCES = \
- oss4-common.h \
- oss4-backend.c \
- oss4-backend.h \
- oss4-device.c \
- oss4-device.h
-
-libmatemixer_oss4_la_LIBADD = \
- $(GLIB_LIBS)
-
-libmatemixer_oss4_la_LDFLAGS = \
- -avoid-version \
- -no-undefined \
- -export-dynamic \
- -module
-
--include $(top_srcdir)/git.mk
diff --git a/backends/oss4/oss4-backend.c b/backends/oss4/oss4-backend.c
deleted file mode 100644
index bd79fdf..0000000
--- a/backends/oss4/oss4-backend.c
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Copyright (C) 2014 Michal Ratajsky <[email protected]>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <glib.h>
-#include <glib-object.h>
-#include <glib/gstdio.h>
-#include <glib/gprintf.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-
-#include <libmatemixer/matemixer-backend.h>
-#include <libmatemixer/matemixer-backend-module.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-stream.h>
-
-#include "oss4-backend.h"
-#include "oss4-common.h"
-#include "oss4-device.h"
-
-#define BACKEND_NAME "OSS4"
-#define BACKEND_PRIORITY 8
-
-#define PATH_SNDSTAT "/dev/sndstat"
-
-struct _Oss4BackendPrivate
-{
- gint fd;
- gchar *sndstat;
- GHashTable *devices;
- GHashTable *streams;
- MateMixerStream *default_input;
- MateMixerStream *default_output;
- MateMixerState state;
-};
-
-enum {
- PROP_0,
- PROP_STATE,
- PROP_DEFAULT_INPUT,
- PROP_DEFAULT_OUTPUT
-};
-
-static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface);
-
-static void oss4_backend_class_init (Oss4BackendClass *klass);
-static void oss4_backend_class_finalize (Oss4BackendClass *klass);
-
-static void oss4_backend_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec);
-
-static void oss4_backend_init (Oss4Backend *oss);
-static void oss4_backend_dispose (GObject *object);
-static void oss4_backend_finalize (GObject *object);
-
-G_DEFINE_DYNAMIC_TYPE_EXTENDED (Oss4Backend, oss4_backend,
- G_TYPE_OBJECT, 0,
- G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND,
- mate_mixer_backend_interface_init))
-
-static gboolean oss4_backend_open (MateMixerBackend *backend);
-static void oss4_backend_close (MateMixerBackend *backend);
-static GList * oss4_backend_list_devices (MateMixerBackend *backend);
-static GList * oss4_backend_list_streams (MateMixerBackend *backend);
-
-static void change_state (Oss4Backend *oss,
- MateMixerState state);
-
-static gboolean read_device (Oss4Backend *oss, gint index);
-
-static gchar * read_device_sndstat_description (Oss4Backend *oss,
- const gchar *prefix);
-
-static void add_device (Oss4Backend *oss, Oss4Device *device);
-static void remove_device (Oss4Backend *oss, Oss4Device *device);
-
-static MateMixerBackendInfo info;
-
-void
-backend_module_init (GTypeModule *module)
-{
- oss4_backend_register_type (module);
-
- info.name = BACKEND_NAME;
- info.priority = BACKEND_PRIORITY;
- info.g_type = OSS4_TYPE_BACKEND;
- info.backend_type = MATE_MIXER_BACKEND_OSS4;
-}
-
-const MateMixerBackendInfo *backend_module_get_info (void)
-{
- return &info;
-}
-
-static void
-mate_mixer_backend_interface_init (MateMixerBackendInterface *iface)
-{
- iface->open = oss4_backend_open;
- iface->close = oss4_backend_close;
- iface->list_devices = oss4_backend_list_devices;
- iface->list_streams = oss4_backend_list_streams;
-}
-
-static void
-oss4_backend_class_init (Oss4BackendClass *klass)
-{
- GObjectClass *object_class;
-
- object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = oss4_backend_dispose;
- object_class->finalize = oss4_backend_finalize;
- object_class->get_property = oss4_backend_get_property;
-
- 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");
-
- g_type_class_add_private (object_class, sizeof (Oss4BackendPrivate));
-}
-
-/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */
-static void
-oss4_backend_class_finalize (Oss4BackendClass *klass)
-{
-}
-
-static void
-oss4_backend_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec)
-{
- Oss4Backend *oss;
-
- oss = OSS4_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
-oss4_backend_init (Oss4Backend *oss)
-{
- oss->priv = G_TYPE_INSTANCE_GET_PRIVATE (oss,
- OSS4_TYPE_BACKEND,
- Oss4BackendPrivate);
-
- oss->priv->devices = g_hash_table_new_full (g_direct_hash,
- g_direct_equal,
- NULL,
- g_object_unref);
-
- oss->priv->streams = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-}
-
-static void
-oss4_backend_dispose (GObject *object)
-{
- oss4_backend_close (MATE_MIXER_BACKEND (object));
-}
-
-static void
-oss4_backend_finalize (GObject *object)
-{
- Oss4Backend *oss;
-
- oss = OSS4_BACKEND (object);
-
- g_hash_table_destroy (oss->priv->devices);
- g_hash_table_destroy (oss->priv->streams);
-}
-
-static gboolean
-oss4_backend_open (MateMixerBackend *backend)
-{
- Oss4Backend *oss;
- gint fd;
- gint i;
- gint ret;
- struct oss_sysinfo info;
-
- g_return_val_if_fail (OSS4_IS_BACKEND (backend), FALSE);
-
- oss = OSS4_BACKEND (backend);
-
- fd = g_open ("/dev/mixer", O_RDONLY, 0);
- if (fd == -1)
- fd = g_open ("/dev/mixer0", O_RDONLY, 0);
- if (fd == -1) {
- change_state (oss, MATE_MIXER_STATE_FAILED);
- return FALSE;
- }
-
- /* Query the number of mixer devices */
- ret = ioctl (fd, OSS_SYSINFO, &info);
- if (ret == -1) {
- close (fd);
- change_state (oss, MATE_MIXER_STATE_FAILED);
- return FALSE;
- }
-
- g_debug ("The sound system is %s version %s",
- info.product,
- info.version);
-
-#if !defined(__linux__)
- /* 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 = PATH_SNDSTAT;
-#endif
-
- oss->priv->fd = fd;
-
- for (i = 0; i < info.nummixers; i++)
- read_device (oss, i);
-
- change_state (oss, MATE_MIXER_STATE_READY);
- return TRUE;
-}
-
-void
-oss4_backend_close (MateMixerBackend *backend)
-{
- Oss4Backend *oss;
-
- g_return_if_fail (OSS4_IS_BACKEND (backend));
-
- oss = OSS4_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_hash_table_remove_all (oss->priv->devices);
-}
-
-static GList *
-oss4_backend_list_devices (MateMixerBackend *backend)
-{
- GList *list;
-
- g_return_val_if_fail (OSS4_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 (OSS4_BACKEND (backend)->priv->devices);
- if (list != NULL) {
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
- }
- return NULL;
-}
-
-static GList *
-oss4_backend_list_streams (MateMixerBackend *backend)
-{
- GList *list;
-
- g_return_val_if_fail (OSS4_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 (OSS4_BACKEND (backend)->priv->streams);
- if (list != NULL) {
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
- }
- return NULL;
-}
-
-static void
-change_state (Oss4Backend *oss, MateMixerState state)
-{
- if (oss->priv->state == state)
- return;
-
- oss->priv->state = state;
-
- g_object_notify (G_OBJECT (oss), "state");
-}
-
-static gboolean
-read_device (Oss4Backend *oss, gint index)
-{
- Oss4Device *device;
- gboolean ret;
- gchar *description = NULL;
- struct oss_mixerinfo info;
-
- /* We assume that the name and capabilities of a device do not change */
- device = g_hash_table_lookup (oss->priv->devices, GINT_TO_POINTER (index));
- if (G_UNLIKELY (device != NULL)) {
- g_debug ("Attempt to re-read already know device with index %d", index);
- return TRUE;
- }
-
- info.dev = index;
- ret = ioctl (oss->priv->fd, SNDCTL_MIXERINFO, &info);
- if (ret == -1) {
- g_debug ("Failed to read mixer info: %s", g_strerror (errno));
- return FALSE;
- }
-
- if (info.enabled == 0)
- return TRUE;
-
- /* Use the id as the device name and try to figure out the name of the
- * sound card from the sndstat file if it is available, otherwise use
- * the name from the mixer info */
- if (oss->priv->sndstat != NULL &&
- g_str_has_prefix (info.name, "pcm") == TRUE)
- description = read_device_sndstat_description (oss, info.name);
-
- if (description == NULL)
- description = g_strdup (info.name);
-
- device = oss4_device_new (info.id, description, oss->priv->fd, index);
-
- ret = oss4_device_read (device);
- if (ret == TRUE)
- add_device (oss, device);
-
- g_object_unref (device);
- g_free (description);
-
- return ret;
-}
-
-static gchar *
-read_device_sndstat_description (Oss4Backend *oss, const gchar *prefix)
-{
- FILE *fp;
- gchar line[256];
- gchar *description = NULL;
-
- g_debug ("reading prefix %s", prefix);
-
- fp = fopen (oss->priv->sndstat, "r");
- if (fp == NULL) {
- g_warning ("Failed to read %s: %s", oss->priv->sndstat, g_strerror (errno));
- return FALSE;
- }
-
- while (fgets (line, sizeof (line), fp) != NULL) {
- gchar *p;
-
- if (g_str_has_prefix (line, prefix) == FALSE)
- continue;
-
- /* Example line:
- * pcm0: <ATI R6xx (HDMI)> (play) default */
- p = strchr (line, '<');
- if (p != NULL && *p && *(++p)) {
- gchar *end = strchr (p, '>');
-
- if (end != NULL)
- description = 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)"))
- ;
-
- if (description != NULL)
- break;
- }
-
- fclose (fp);
- return description;
-}
-
-static void
-add_device (Oss4Backend *oss, Oss4Device *device)
-{
- MateMixerStream *stream;
-
- /* 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,
- GINT_TO_POINTER (oss4_device_get_index (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 = oss4_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));
- }
-
- stream = oss4_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),
- "stream-added",
- mate_mixer_stream_get_name (stream));
- }
-}
-
-static void
-remove_device (Oss4Backend *oss, Oss4Device *device)
-{
- MateMixerStream *stream;
-
- /* Remove the device streams first as they are a part of the device */
- stream = oss4_device_get_input_stream (device);
- if (stream != NULL) {
- const gchar *name = mate_mixer_stream_get_name (stream);
-
- g_hash_table_remove (oss->priv->streams, name);
- g_signal_emit_by_name (G_OBJECT (oss),
- "stream-removed",
- name);
- }
-
- stream = oss4_device_get_output_stream (device);
- if (stream != NULL) {
- const gchar *name = mate_mixer_stream_get_name (stream);
-
- g_hash_table_remove (oss->priv->streams, stream);
- g_signal_emit_by_name (G_OBJECT (oss),
- "stream-removed",
- name);
- }
-
- /* Remove the device */
- g_object_ref (device);
-
- g_hash_table_remove (oss->priv->devices,
- GINT_TO_POINTER (oss4_device_get_index (device)));
-
- g_signal_emit_by_name (G_OBJECT (oss),
- "device-removed",
- mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
-
- g_object_unref (device);
-}
diff --git a/backends/oss4/oss4-backend.h b/backends/oss4/oss4-backend.h
deleted file mode 100644
index bc89e72..0000000
--- a/backends/oss4/oss4-backend.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2014 Michal Ratajsky <[email protected]>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef OSS4_BACKEND_H
-#define OSS4_BACKEND_H
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include <libmatemixer/matemixer-backend-module.h>
-
-#define OSS4_TYPE_BACKEND \
- (oss4_backend_get_type ())
-#define OSS4_BACKEND(o) \
- (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS4_TYPE_BACKEND, Oss4Backend))
-#define OSS4_IS_BACKEND(o) \
- (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS4_TYPE_BACKEND))
-#define OSS4_BACKEND_CLASS(k) \
- (G_TYPE_CHECK_CLASS_CAST ((k), OSS4_TYPE_BACKEND, Oss4BackendClass))
-#define OSS4_IS_BACKEND_CLASS(k) \
- (G_TYPE_CHECK_CLASS_TYPE ((k), OSS4_TYPE_BACKEND))
-#define OSS4_BACKEND_GET_CLASS(o) \
- (G_TYPE_INSTANCE_GET_CLASS ((o), OSS4_TYPE_BACKEND, Oss4BackendClass))
-
-typedef struct _Oss4Backend Oss4Backend;
-typedef struct _Oss4BackendClass Oss4BackendClass;
-typedef struct _Oss4BackendPrivate Oss4BackendPrivate;
-
-struct _Oss4Backend
-{
- GObject parent;
-
- /*< private >*/
- Oss4BackendPrivate *priv;
-};
-
-struct _Oss4BackendClass
-{
- GObjectClass parent_class;
-};
-
-GType oss4_backend_get_type (void) G_GNUC_CONST;
-
-/* Support function for dynamic loading of the backend module */
-void backend_module_init (GTypeModule *module);
-const MateMixerBackendInfo *backend_module_get_info (void);
-
-#endif /* OSS4_BACKEND_H */
diff --git a/backends/oss4/oss4-device.c b/backends/oss4/oss4-device.c
deleted file mode 100644
index b68648b..0000000
--- a/backends/oss4/oss4-device.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2014 Michal Ratajsky <[email protected]>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <errno.h>
-#include <unistd.h>
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-#include <glib-object.h>
-
-#include <libmatemixer/matemixer-device.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-port.h>
-#include <libmatemixer/matemixer-port-private.h>
-#include <libmatemixer/matemixer-stream.h>
-#include <libmatemixer/matemixer-stream-control.h>
-
-#include "oss4-common.h"
-#include "oss4-device.h"
-
-#define OSS4_DEVICE_ICON "audio-card"
-
-struct _Oss4DevicePrivate
-{
- gint fd;
- gint index;
- gchar *name;
- gchar *description;
- gchar *icon;
- MateMixerStream *input;
- MateMixerStream *output;
-};
-
-enum {
- PROP_0,
- PROP_NAME,
- PROP_DESCRIPTION,
- PROP_ICON,
- PROP_ACTIVE_PROFILE,
- PROP_FD,
- PROP_INDEX
-};
-
-static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface);
-
-static void oss4_device_class_init (Oss4DeviceClass *klass);
-
-static void oss4_device_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec);
-static void oss4_device_set_property (GObject *object,
- guint param_id,
- const GValue *value,
- GParamSpec *pspec);
-
-static void oss4_device_init (Oss4Device *device);
-static void oss4_device_finalize (GObject *object);
-
-G_DEFINE_TYPE_WITH_CODE (Oss4Device, oss4_device, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE,
- mate_mixer_device_interface_init))
-
-static const gchar *oss4_device_get_name (MateMixerDevice *device);
-static const gchar *oss4_device_get_description (MateMixerDevice *device);
-static const gchar *oss4_device_get_icon (MateMixerDevice *device);
-
-static gboolean read_mixer_devices (Oss4Device *device);
-
-static void
-mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)
-{
- iface->get_name = oss4_device_get_name;
- iface->get_description = oss4_device_get_description;
- iface->get_icon = oss4_device_get_icon;
-}
-
-static void
-oss4_device_class_init (Oss4DeviceClass *klass)
-{
- GObjectClass *object_class;
-
- object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = oss4_device_finalize;
- object_class->get_property = oss4_device_get_property;
- object_class->set_property = oss4_device_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_INDEX,
- g_param_spec_int ("index",
- "Index",
- "Index of the device",
- G_MININT,
- G_MAXINT,
- 0,
- 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 (Oss4DevicePrivate));
-}
-
-static void
-oss4_device_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec)
-{
- Oss4Device *device;
-
- device = OSS4_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, OSS4_DEVICE_ICON);
- break;
- case PROP_ACTIVE_PROFILE:
- /* Not supported */
- g_value_set_object (value, NULL);
- break;
- case PROP_FD:
- g_value_set_int (value, device->priv->fd);
- break;
- case PROP_INDEX:
- g_value_set_int (value, device->priv->index);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
- break;
- }
-}
-
-static void
-oss4_device_set_property (GObject *object,
- guint param_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- Oss4Device *device;
-
- device = OSS4_DEVICE (object);
-
- switch (param_id) {
- case PROP_NAME:
- /* Construct-only string */
- device->priv->name = g_strdup (g_value_get_string (value));
- break;
- case PROP_DESCRIPTION:
- /* Construct-only string */
- device->priv->description = g_strdup (g_value_get_string (value));
- break;
- case PROP_ICON:
- /* Construct-only string */
- device->priv->icon = g_strdup (g_value_get_string (value));
- break;
- case PROP_FD:
- device->priv->fd = dup (g_value_get_int (value));
- break;
- case PROP_INDEX:
- device->priv->index = g_value_get_int (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
- break;
- }
-}
-
-static void
-oss4_device_init (Oss4Device *device)
-{
- device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
- OSS4_TYPE_DEVICE,
- Oss4DevicePrivate);
-}
-
-static void
-oss4_device_finalize (GObject *object)
-{
- Oss4Device *device;
-
- device = OSS4_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 (oss4_device_parent_class)->finalize (object);
-}
-
-Oss4Device *
-oss4_device_new (const gchar *name,
- const gchar *description,
- gint fd,
- gint index)
-{
- Oss4Device *device;
-
- device = g_object_new (OSS4_TYPE_DEVICE,
- "name", name,
- "description", description,
- "fd", fd,
- "index", index,
- NULL);
-
- return device;
-}
-
-gboolean
-oss4_device_read (Oss4Device *odevice)
-{
- gint exts;
- gint ret;
- gint i;
-
- ret = ioctl (odevice->priv->fd, SNDCTL_MIX_NREXT, &exts);
- if (ret == -1)
- return FALSE;
-
- for (i = 0; i < exts; i++) {
- oss_mixext ext;
-
- ext.dev = odevice->priv->index;
- ext.ctrl = i;
- ret = ioctl (odevice->priv->fd, SNDCTL_MIX_EXTINFO, &ext);
- if (ret == -1)
- continue;
-
- g_debug ("Mixer control %d type %d\n"
- " min %d max %d\n"
- " id %s\n"
- " extname %s",
- i,ext.type, ext.minvalue, ext.maxvalue, ext.id, ext.extname);
- }
-
- return TRUE;
-}
-
-gint
-oss4_device_get_index (Oss4Device *odevice)
-{
- g_return_val_if_fail (OSS4_IS_DEVICE (odevice), FALSE);
-
- return odevice->priv->index;
-}
-
-MateMixerStream *
-oss4_device_get_input_stream (Oss4Device *odevice)
-{
- g_return_val_if_fail (OSS4_IS_DEVICE (odevice), FALSE);
-
- return odevice->priv->input;
-}
-
-MateMixerStream *
-oss4_device_get_output_stream (Oss4Device *odevice)
-{
- g_return_val_if_fail (OSS4_IS_DEVICE (odevice), FALSE);
-
- return odevice->priv->output;
-}
-
-static gboolean
-read_mixer_devices (Oss4Device *device)
-{
-}
-
-static const gchar *
-oss4_device_get_name (MateMixerDevice *device)
-{
- g_return_val_if_fail (OSS4_IS_DEVICE (device), NULL);
-
- return OSS4_DEVICE (device)->priv->name;
-}
-
-static const gchar *
-oss4_device_get_description (MateMixerDevice *device)
-{
- g_return_val_if_fail (OSS4_IS_DEVICE (device), NULL);
-
- return OSS4_DEVICE (device)->priv->description;
-}
-
-static const gchar *
-oss4_device_get_icon (MateMixerDevice *device)
-{
- g_return_val_if_fail (OSS4_IS_DEVICE (device), NULL);
-
- return OSS4_DEVICE_ICON;
-}
diff --git a/backends/oss4/oss4-device.h b/backends/oss4/oss4-device.h
deleted file mode 100644
index 3ec72e7..0000000
--- a/backends/oss4/oss4-device.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2014 Michal Ratajsky <[email protected]>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef OSS4_DEVICE_H
-#define OSS4_DEVICE_H
-
-#include <glib.h>
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define OSS4_TYPE_DEVICE \
- (oss4_device_get_type ())
-#define OSS4_DEVICE(o) \
- (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS4_TYPE_DEVICE, Oss4Device))
-#define OSS4_IS_DEVICE(o) \
- (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS4_TYPE_DEVICE))
-#define OSS4_DEVICE_CLASS(k) \
- (G_TYPE_CHECK_CLASS_CAST ((k), OSS4_TYPE_DEVICE, Oss4DeviceClass))
-#define OSS4_IS_DEVICE_CLASS(k) \
- (G_TYPE_CHECK_CLASS_TYPE ((k), OSS4_TYPE_DEVICE))
-#define OSS4_DEVICE_GET_CLASS(o) \
- (G_TYPE_INSTANCE_GET_CLASS ((o), OSS4_TYPE_DEVICE, Oss4DeviceClass))
-
-typedef struct _Oss4Device Oss4Device;
-typedef struct _Oss4DeviceClass Oss4DeviceClass;
-typedef struct _Oss4DevicePrivate Oss4DevicePrivate;
-
-struct _Oss4Device
-{
- GObject parent;
-
- /*< private >*/
- Oss4DevicePrivate *priv;
-};
-
-struct _Oss4DeviceClass
-{
- GObjectClass parent;
-};
-
-GType oss4_device_get_type (void) G_GNUC_CONST;
-
-Oss4Device * oss4_device_new (const gchar *name,
- const gchar *description,
- gint fd,
- gint index);
-
-gboolean oss4_device_read (Oss4Device *device);
-
-gint oss4_device_get_index (Oss4Device *odevice);
-
-MateMixerStream *oss4_device_get_input_stream (Oss4Device *odevice);
-MateMixerStream *oss4_device_get_output_stream (Oss4Device *odevice);
-
-G_END_DECLS
-
-#endif /* OSS_DEVICE_H */