From 0bc3aa762cd794da510f03229840d939ee7bc0c9 Mon Sep 17 00:00:00 2001 From: Michal Ratajsky Date: Fri, 18 Jul 2014 16:51:07 +0200 Subject: Add module skeleton and build support --- backends/Makefile.am | 4 ++ backends/oss/Makefile.am | 26 ++++++++ backends/oss/oss-backend.c | 136 +++++++++++++++++++++++++++++++++++++++++ backends/oss/oss-backend.h | 58 ++++++++++++++++++ configure.ac | 36 +++++++++++ libmatemixer/matemixer-enums.h | 2 + 6 files changed, 262 insertions(+) create mode 100644 backends/oss/Makefile.am create mode 100644 backends/oss/oss-backend.c create mode 100644 backends/oss/oss-backend.h diff --git a/backends/Makefile.am b/backends/Makefile.am index e223042..8ca01bd 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -8,4 +8,8 @@ if HAVE_PULSEAUDIO SUBDIRS += pulse endif +if HAVE_OSS +SUBDIRS += oss +endif + -include $(top_srcdir)/git.mk diff --git a/backends/oss/Makefile.am b/backends/oss/Makefile.am new file mode 100644 index 0000000..3f0b8ea --- /dev/null +++ b/backends/oss/Makefile.am @@ -0,0 +1,26 @@ +backenddir = $(libdir)/libmatemixer + +backend_LTLIBRARIES = libmatemixer-oss.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -DG_LOG_DOMAIN=\"libmatemixer-oss\" + +libmatemixer_oss_la_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(OSS_CFLAGS) + +libmatemixer_oss_la_SOURCES = \ + oss-backend.c \ + oss-backend.h + +libmatemixer_oss_la_LIBADD = \ + $(GLIB_LIBS) + +libmatemixer_oss_la_LDFLAGS = \ + -avoid-version \ + -no-undefined \ + -export-dynamic \ + -module + +-include $(top_srcdir)/git.mk diff --git a/backends/oss/oss-backend.c b/backends/oss/oss-backend.c new file mode 100644 index 0000000..22d3547 --- /dev/null +++ b/backends/oss/oss-backend.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include + +#include +#include +#include + +#include "oss-backend.h" + +#define BACKEND_NAME "OSS" +#define BACKEND_PRIORITY 90 + +enum { + PROP_0, + PROP_STATE, + PROP_DEFAULT_INPUT, + PROP_DEFAULT_OUTPUT, + N_PROPERTIES +}; + +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); + +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)) + +static gboolean backend_open (MateMixerBackend *backend); +static MateMixerState backend_get_state (MateMixerBackend *backend); + +static MateMixerBackendInfo info; + +void +backend_module_init (GTypeModule *module) +{ + oss_backend_register_type (module); + + info.name = BACKEND_NAME; + info.priority = BACKEND_PRIORITY; + info.g_type = OSS_TYPE_BACKEND; + info.backend_type = MATE_MIXER_BACKEND_OSS; +} + +const MateMixerBackendInfo * +backend_module_get_info (void) +{ + return &info; +} + +static void +mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) +{ + iface->open = backend_open; + iface->get_state = backend_get_state; +} + +static void +oss_backend_class_init (OssBackendClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = oss_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"); +} + +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ +static void +oss_backend_class_finalize (OssBackendClass *klass) +{ +} + +static void +oss_backend_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + switch (param_id) { + case PROP_STATE: + g_value_set_enum (value, MATE_MIXER_STATE_READY); + break; + case PROP_DEFAULT_INPUT: + case PROP_DEFAULT_OUTPUT: + g_value_set_object (value, NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +oss_backend_init (OssBackend *oss) +{ +} + +static gboolean +backend_open (MateMixerBackend *backend) +{ + return TRUE; +} + +static MateMixerState +backend_get_state (MateMixerBackend *backend) +{ + return MATE_MIXER_STATE_READY; +} diff --git a/backends/oss/oss-backend.h b/backends/oss/oss-backend.h new file mode 100644 index 0000000..02567ed --- /dev/null +++ b/backends/oss/oss-backend.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef OSS_BACKEND_H +#define OSS_BACKEND_H + +#include +#include + +#include + +#define OSS_TYPE_BACKEND \ + (oss_backend_get_type ()) +#define OSS_BACKEND(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS_TYPE_BACKEND, OssBackend)) +#define OSS_IS_BACKEND(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS_TYPE_BACKEND)) +#define OSS_BACKEND_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), OSS_TYPE_BACKEND, OssBackendClass)) +#define OSS_IS_BACKEND_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), OSS_TYPE_BACKEND)) +#define OSS_BACKEND_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_BACKEND, OssBackendClass)) + +typedef struct _OssBackend OssBackend; +typedef struct _OssBackendClass OssBackendClass; + +struct _OssBackend +{ + GObject parent; +}; + +struct _OssBackendClass +{ + GObjectClass parent_class; +}; + +GType oss_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 /* OSS_BACKEND_H */ diff --git a/configure.ac b/configure.ac index 3adfcba..d9bffc6 100644 --- a/configure.ac +++ b/configure.ac @@ -118,6 +118,40 @@ AC_SUBST(HAVE_PULSEAUDIO) AC_SUBST(PULSEAUDIO_CFLAGS) AC_SUBST(PULSEAUDIO_LIBS) +# ----------------------------------------------------------------------- +# OSS +# ----------------------------------------------------------------------- +AC_ARG_ENABLE([oss], + AS_HELP_STRING([--enable-oss], + [Enable OSS backend module @<:@default=no@:>@]), + enable_oss=$enableval, enable_oss=no) + +if test "x$enable_oss" != "xno"; then + AC_CHECK_HEADERS([soundcard.h sys/soundcard.h machine/soundcard.h]) + if test "x$ac_cv_header_soundcard_h" = "xyes" -o \ + "x$ac_cv_header_sys_soundcard_h" = "xyes" -o \ + "x$ac_cv_header_machine_soundcard_h" = "xyes"; then + have_oss=yes + else + have_oss=no + fi + + if test "x$enable_oss" = "xyes" -a "x$have_oss" = "xno"; then + AC_MSG_ERROR([OSS support explicitly requested but dependencies not found]) + fi + + if test "x$have_oss" = "xyes" ; then + AC_DEFINE(HAVE_OSS, [], [Define if we have OSS support]) + fi +else + have_oss=no +fi + +AM_CONDITIONAL(HAVE_OSS, test "x$have_oss" = "xyes") + +AC_SUBST(HAVE_OSS) +AC_SUBST(OSS_CFLAGS) + # ======================================================================= # Compiler warnings # ======================================================================= @@ -169,6 +203,7 @@ Makefile libmatemixer/Makefile backends/Makefile backends/null/Makefile +backends/oss/Makefile backends/pulse/Makefile data/Makefile data/libmatemixer.pc @@ -193,5 +228,6 @@ echo " Build Null module: $have_null Build PulseAudio module: $have_pulseaudio + Build OSS module: $have_oss " diff --git a/libmatemixer/matemixer-enums.h b/libmatemixer/matemixer-enums.h index a6326ce..7e523bb 100644 --- a/libmatemixer/matemixer-enums.h +++ b/libmatemixer/matemixer-enums.h @@ -47,6 +47,7 @@ typedef enum { * PulseAudio sound system backend. It has the highest priority and * will be the first one to try unless you select a specific backend * to connect to. + * @MATE_MIXER_BACKEND_OSS: * @MATE_MIXER_BACKEND_NULL: * Fallback backend which never fails to initialize, but provides no * functionality. This backend has the lowest priority and will be used @@ -56,6 +57,7 @@ typedef enum { typedef enum { MATE_MIXER_BACKEND_UNKNOWN, MATE_MIXER_BACKEND_PULSEAUDIO, + MATE_MIXER_BACKEND_OSS, MATE_MIXER_BACKEND_NULL } MateMixerBackendType; -- cgit v1.2.1 From 59a9aabf7b66e130f68b797c5a3674798fae437b Mon Sep 17 00:00:00 2001 From: Michal Ratajsky Date: Fri, 25 Jul 2014 23:47:46 +0200 Subject: Support OSS --- backends/oss/Makefile.am | 12 +- backends/oss/oss-backend.c | 498 +++++++++++++++++++++++++++++- backends/oss/oss-backend.h | 8 +- backends/oss/oss-common.h | 38 +++ backends/oss/oss-device.c | 525 +++++++++++++++++++++++++++++++ backends/oss/oss-device.h | 73 +++++ backends/oss/oss-stream-control.c | 529 ++++++++++++++++++++++++++++++++ backends/oss/oss-stream-control.h | 74 +++++ backends/oss/oss-stream.c | 315 +++++++++++++++++++ backends/oss/oss-stream.h | 75 +++++ configure.ac | 3 + examples/monitor.c | 56 ++-- libmatemixer/Makefile.am | 4 +- libmatemixer/matemixer-backend.c | 33 +- libmatemixer/matemixer-enum-types.c | 31 +- libmatemixer/matemixer-enum-types.h | 3 + libmatemixer/matemixer-enums.h | 55 +++- libmatemixer/matemixer-stream-control.c | 445 +++++++++++++++++++++++++++ libmatemixer/matemixer-stream-control.h | 148 +++++++++ libmatemixer/matemixer-stream.c | 475 +++++----------------------- libmatemixer/matemixer-stream.h | 156 +++------- 21 files changed, 2974 insertions(+), 582 deletions(-) create mode 100644 backends/oss/oss-common.h create mode 100644 backends/oss/oss-device.c create mode 100644 backends/oss/oss-device.h create mode 100644 backends/oss/oss-stream-control.c create mode 100644 backends/oss/oss-stream-control.h create mode 100644 backends/oss/oss-stream.c create mode 100644 backends/oss/oss-stream.h create mode 100644 libmatemixer/matemixer-stream-control.c create mode 100644 libmatemixer/matemixer-stream-control.h diff --git a/backends/oss/Makefile.am b/backends/oss/Makefile.am index 3f0b8ea..44caeb8 100644 --- a/backends/oss/Makefile.am +++ b/backends/oss/Makefile.am @@ -11,11 +11,19 @@ libmatemixer_oss_la_CFLAGS = \ $(OSS_CFLAGS) libmatemixer_oss_la_SOURCES = \ + oss-common.h \ oss-backend.c \ - oss-backend.h + oss-backend.h \ + oss-device.c \ + oss-device.h \ + oss-stream.c \ + oss-stream.h \ + oss-stream-control.c \ + oss-stream-control.h libmatemixer_oss_la_LIBADD = \ - $(GLIB_LIBS) + $(GLIB_LIBS) \ + $(OSS_LIBS) libmatemixer_oss_la_LDFLAGS = \ -avoid-version \ diff --git a/backends/oss/oss-backend.c b/backends/oss/oss-backend.c index 22d3547..6e058f8 100644 --- a/backends/oss/oss-backend.c +++ b/backends/oss/oss-backend.c @@ -15,43 +15,95 @@ * License along with this library; if not, see . */ +#include +#include +#include #include #include +#include +#include +#include +#include #include #include #include +#include #include "oss-backend.h" +#include "oss-common.h" +#include "oss-device.h" #define BACKEND_NAME "OSS" -#define BACKEND_PRIORITY 90 +#define BACKEND_PRIORITY 9 + +#define PATH_SNDSTAT "/dev/sndstat" + +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, - N_PROPERTIES + PROP_DEFAULT_OUTPUT }; 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)) -static gboolean backend_open (MateMixerBackend *backend); -static MateMixerState backend_get_state (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 void change_state (OssBackend *oss, + MateMixerState state); + +static void on_dev_monitor_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + OssBackend *oss); + +static gboolean read_device (OssBackend *oss, + const gchar *path); + +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 MateMixerBackendInfo info; @@ -66,8 +118,7 @@ backend_module_init (GTypeModule *module) info.backend_type = MATE_MIXER_BACKEND_OSS; } -const MateMixerBackendInfo * -backend_module_get_info (void) +const MateMixerBackendInfo *backend_module_get_info (void) { return &info; } @@ -75,8 +126,10 @@ backend_module_get_info (void) static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) { - iface->open = backend_open; - iface->get_state = backend_get_state; + 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 @@ -85,11 +138,15 @@ oss_backend_class_init (OssBackendClass *klass) GObjectClass *object_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; 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 (OssBackendPrivate)); } /* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ @@ -104,13 +161,20 @@ oss_backend_get_property (GObject *object, GValue *value, GParamSpec *pspec) { + OssBackend *oss; + + oss = OSS_BACKEND (object); + switch (param_id) { case PROP_STATE: - g_value_set_enum (value, MATE_MIXER_STATE_READY); + 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, NULL); + g_value_set_object (value, oss->priv->default_output); + break; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -121,16 +185,420 @@ oss_backend_get_property (GObject *object, static void oss_backend_init (OssBackend *oss) { + oss->priv = G_TYPE_INSTANCE_GET_PRIVATE (oss, + OSS_TYPE_BACKEND, + OssBackendPrivate); + + oss->priv->devices = g_hash_table_new_full (g_str_hash, + 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)); +} + +static void +oss_backend_finalize (GObject *object) +{ + OssBackend *oss; + + oss = OSS_BACKEND (object); + + g_hash_table_destroy (oss->priv->devices); + g_hash_table_destroy (oss->priv->streams); } static gboolean -backend_open (MateMixerBackend *backend) +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); return TRUE; } -static MateMixerState -backend_get_state (MateMixerBackend *backend) +void +oss_backend_close (MateMixerBackend *backend) +{ + OssBackend *oss; + + g_return_if_fail (OSS_IS_BACKEND (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_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); +} + +static GList * +oss_backend_list_devices (MateMixerBackend *backend) +{ + GList *list; + + 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->devices); + if (list != NULL) { + g_list_foreach (list, (GFunc) g_object_ref, NULL); + + return list; + } + return NULL; +} + +static GList * +oss_backend_list_streams (MateMixerBackend *backend) +{ + GList *list; + + 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); + + return list; + } + return NULL; +} + +static void +change_state (OssBackend *oss, MateMixerState state) +{ + if (oss->priv->state == state) + return; + + oss->priv->state = state; + + g_object_notify (G_OBJECT (oss), "state"); +} + +static void +on_dev_monitor_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + OssBackend *oss) +{ + gchar *path; + + if (event_type != G_FILE_MONITOR_EVENT_CREATED && + event_type != G_FILE_MONITOR_EVENT_DELETED) + return; + + path = g_file_get_path (file); + + /* 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); +} + +static gboolean +read_device (OssBackend *oss, const gchar *path) +{ + OssDevice *device; + gint fd; + gboolean ret; + const gchar *description; + + /* 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; + } + + fd = g_open (path, O_RDWR, 0); + if (fd == -1) { + if (errno != ENOENT && errno != ENXIO) + g_debug ("%s: %s", path, g_strerror (errno)); + return FALSE; + } + + device = oss_device_new (path, fd); + + description = read_device_description (oss, path, fd); + if (description != NULL) + oss_device_set_description (device, description); + else + oss_device_set_description (device, _("Unknown device")); + + /* Close the descriptor as the device should dup it if it intends + * to keep it */ + g_close (fd, NULL); + + ret = oss_device_read (device); + if (ret == TRUE) + add_device (oss, device); + + g_object_unref (device); + return ret; +} + +static gchar * +read_device_description (OssBackend *oss, const gchar *path, gint fd) +{ + struct mixer_info info; + + 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); + } + + /* 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); + + g_clear_object (&oss->priv->sndstat); + } + g_error_free (error); + } else + return read_device_sndstat_description (path, input); + } + + return NULL; +} + +static gchar * +read_device_sndstat_description (const gchar *path, GFileInputStream *input) { - return MATE_MIXER_STATE_READY; + gchar *line; + gchar *prefix; + gchar *description = NULL; + GDataInputStream *stream; + + if (G_UNLIKELY (g_str_has_prefix (path, "/dev/mixer")) == FALSE) { + g_warn_if_reached (); + 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; + } + + if (g_str_has_prefix (line, prefix) == FALSE) + continue; + + /* Example line: + * pcm0: (play) default */ + p = strchr (line, '<'); + if (p != NULL && *p && *(++p)) { + gchar *end = strchr (p, '>'); + + if (end != NULL) + description = g_strndup (p, end - p); + } + + // 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; + } + + g_object_unref (stream); + g_free (prefix); + + return description; +} + +static void +add_device (OssBackend *oss, OssDevice *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, + 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)); + } + + 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), + "stream-added", + mate_mixer_stream_get_name (stream)); + } +} + +static void +remove_device (OssBackend *oss, OssDevice *device) +{ + 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); + + g_hash_table_remove (oss->priv->streams, name); + g_signal_emit_by_name (G_OBJECT (oss), + "stream-removed", + name); + } + + stream = oss_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); + } + + path = oss_device_get_path (device); + + /* Remove the device */ + g_object_ref (device); + + 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))); + + g_object_unref (device); } diff --git a/backends/oss/oss-backend.h b/backends/oss/oss-backend.h index 02567ed..b766799 100644 --- a/backends/oss/oss-backend.h +++ b/backends/oss/oss-backend.h @@ -36,12 +36,16 @@ #define OSS_BACKEND_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_BACKEND, OssBackendClass)) -typedef struct _OssBackend OssBackend; -typedef struct _OssBackendClass OssBackendClass; +typedef struct _OssBackend OssBackend; +typedef struct _OssBackendClass OssBackendClass; +typedef struct _OssBackendPrivate OssBackendPrivate; struct _OssBackend { GObject parent; + + /*< private >*/ + OssBackendPrivate *priv; }; struct _OssBackendClass diff --git a/backends/oss/oss-common.h b/backends/oss/oss-common.h new file mode 100644 index 0000000..7251b86 --- /dev/null +++ b/backends/oss/oss-common.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef OSS_COMMON_H +#define OSS_COMMON_H + +#include "config.h" + +#include +#include +#include +#include + +#ifdef HAVE_SYS_SOUNDCARD_H +# include +#elif HAVE_SOUNDCARD_H +# include +#elif HAVE_MACHINE_SOUNDCARD_H +# include +#else +# error "No OSS header file present" +#endif + +#endif /* OSS_COMMON_H */ diff --git a/backends/oss/oss-device.c b/backends/oss/oss-device.c new file mode 100644 index 0000000..f33ff57 --- /dev/null +++ b/backends/oss/oss-device.c @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "oss-common.h" +#include "oss-device.h" +#include "oss-stream.h" +#include "oss-stream-control.h" + +#define OSS_DEVICE_ICON "audio-card" + +typedef struct +{ + gchar *name; + gchar *description; + MateMixerStreamControlRole role; + MateMixerStreamFlags flags; +} OssDeviceName; + +static const OssDeviceName const oss_devices[] = { + { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, MATE_MIXER_STREAM_OUTPUT }, + { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, MATE_MIXER_STREAM_OUTPUT }, + { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, MATE_MIXER_STREAM_OUTPUT }, + { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, + { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, MATE_MIXER_STREAM_OUTPUT }, + { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, MATE_MIXER_STREAM_OUTPUT }, + { "line", N_("Line-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, MATE_MIXER_STREAM_INPUT }, + /* Recording monitor */ + { "mix", N_("Mixer"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "pcm2", N_("PCM-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, MATE_MIXER_STREAM_OUTPUT }, + /* Recording level (master input) */ + { "rec", N_("Record"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, MATE_MIXER_STREAM_INPUT }, + { "igain", N_("In-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, + { "ogain", N_("Out-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "line1", N_("Line-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "line2", N_("Line-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "line3", N_("Line-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "dig1", N_("Digital-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, + { "dig2", N_("Digital-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, + { "dig3", N_("Digital-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, + { "phin", N_("Phone-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "phout", N_("Phone-out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_OUTPUT }, + { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, + { "monitor", N_("Monitor"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "depth", N_("3D-depth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "center", N_("3D-center"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, + { "midi", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT } +}; + +struct _OssDevicePrivate +{ + gint fd; + gchar *path; + gchar *name; + gchar *description; + MateMixerStream *input; + MateMixerStream *output; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_DESCRIPTION, + PROP_ICON, + PROP_ACTIVE_PROFILE, + PROP_PATH, + PROP_FD +}; + +static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); + +static void oss_device_class_init (OssDeviceClass *klass); + +static void oss_device_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void oss_device_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void oss_device_init (OssDevice *device); +static void oss_device_finalize (GObject *object); + +G_DEFINE_TYPE_WITH_CODE (OssDevice, oss_device, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE, + mate_mixer_device_interface_init)) + +static const gchar *oss_device_get_name (MateMixerDevice *device); +static const gchar *oss_device_get_description (MateMixerDevice *device); +static const gchar *oss_device_get_icon (MateMixerDevice *device); + +static gboolean read_mixer_devices (OssDevice *device); + +static void +mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) +{ + iface->get_name = oss_device_get_name; + iface->get_description = oss_device_get_description; + iface->get_icon = oss_device_get_icon; +} + +static void +oss_device_class_init (OssDeviceClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = oss_device_finalize; + object_class->get_property = oss_device_get_property; + object_class->set_property = oss_device_set_property; + + g_object_class_install_property (object_class, + PROP_PATH, + g_param_spec_string ("path", + "Path", + "Path to the device", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_FD, + g_param_spec_int ("fd", + "File descriptor", + "File descriptor of the device", + G_MININT, + G_MAXINT, + -1, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_override_property (object_class, PROP_NAME, "name"); + g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); + g_object_class_override_property (object_class, PROP_ICON, "icon"); + g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile"); + + g_type_class_add_private (object_class, sizeof (OssDevicePrivate)); +} + +static void +oss_device_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + OssDevice *device; + + device = OSS_DEVICE (object); + + switch (param_id) { + case PROP_NAME: + g_value_set_string (value, device->priv->name); + break; + case PROP_DESCRIPTION: + g_value_set_string (value, device->priv->description); + break; + case PROP_ICON: + g_value_set_string (value, OSS_DEVICE_ICON); + break; + case PROP_ACTIVE_PROFILE: + /* Not supported */ + g_value_set_object (value, NULL); + break; + case PROP_PATH: + g_value_set_string (value, device->priv->path); + break; + case PROP_FD: + g_value_set_int (value, device->priv->fd); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +oss_device_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + OssDevice *device; + + device = OSS_DEVICE (object); + + switch (param_id) { + case PROP_PATH: + /* Construct-only string */ + device->priv->path = g_strdup (g_value_get_string (value)); + break; + case PROP_FD: + device->priv->fd = dup (g_value_get_int (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +oss_device_init (OssDevice *device) +{ + device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, + OSS_TYPE_DEVICE, + OssDevicePrivate); +} + +static void +oss_device_finalize (GObject *object) +{ + OssDevice *device; + + device = OSS_DEVICE (object); + + g_free (device->priv->name); + g_free (device->priv->description); + + if (device->priv->fd != -1) + g_close (device->priv->fd, NULL); + + G_OBJECT_CLASS (oss_device_parent_class)->finalize (object); +} + +OssDevice * +oss_device_new (const gchar *path, gint fd) +{ + OssDevice *device; + gchar *basename; + + g_return_val_if_fail (path != NULL, NULL); + + device = g_object_new (OSS_TYPE_DEVICE, + "path", path, + "fd", fd, + NULL); + + basename = g_path_get_basename (path); + + device->priv->name = g_strdup_printf ("oss-%s", basename); + g_free (basename); + + return device; +} + +gboolean +oss_device_read (OssDevice *device) +{ + gchar *name; + gchar *basename; + MateMixerStreamControl *ctl; + + g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); + + // XXX avoid calling this function repeatedly + + g_debug ("Querying device %s (%s)", + device->priv->path, + device->priv->description); + + basename = g_path_get_basename (device->priv->path); + + name = g_strdup_printf ("oss-input-%s", basename); + device->priv->input = MATE_MIXER_STREAM (oss_stream_new (name, + device->priv->description, + MATE_MIXER_STREAM_INPUT)); + g_free (name); + + name = g_strdup_printf ("oss-output-%s", basename); + device->priv->output = MATE_MIXER_STREAM (oss_stream_new (name, + device->priv->description, + MATE_MIXER_STREAM_OUTPUT | + MATE_MIXER_STREAM_PORTS_FIXED)); + g_free (name); + + if (read_mixer_devices (device) == FALSE) + return FALSE; + + // XXX prefer active ports as default if there is no default + + ctl = mate_mixer_stream_get_default_control (device->priv->input); + if (ctl == NULL) { + const GList *list = mate_mixer_stream_list_controls (device->priv->input); + + if (list != NULL) { + ctl = MATE_MIXER_STREAM_CONTROL (list->data); + oss_stream_set_default_control (OSS_STREAM (device->priv->input), + OSS_STREAM_CONTROL (ctl)); + } else + g_clear_object (&device->priv->input); + } + + if (ctl != NULL) + g_debug ("Default input stream control is %s", + mate_mixer_stream_control_get_description (ctl)); + + ctl = mate_mixer_stream_get_default_control (device->priv->output); + if (ctl == NULL) { + const GList *list = mate_mixer_stream_list_controls (device->priv->output); + + if (list != NULL) { + ctl = MATE_MIXER_STREAM_CONTROL (list->data); + oss_stream_set_default_control (OSS_STREAM (device->priv->output), + OSS_STREAM_CONTROL (ctl)); + } else + g_clear_object (&device->priv->output); + } + + if (ctl != NULL) + g_debug ("Default output stream control is %s", + mate_mixer_stream_control_get_description (ctl)); + + return TRUE; +} + +const gchar * +oss_device_get_path (OssDevice *odevice) +{ + g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + + return odevice->priv->path; +} + +MateMixerStream * +oss_device_get_input_stream (OssDevice *odevice) +{ + g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + + return odevice->priv->input; +} + +MateMixerStream * +oss_device_get_output_stream (OssDevice *odevice) +{ + g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + + return odevice->priv->output; +} + +gboolean +oss_device_set_description (OssDevice *odevice, const gchar *description) +{ + g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + + if (g_strcmp0 (odevice->priv->description, description) != 0) { + g_free (odevice->priv->description); + + odevice->priv->description = g_strdup (description); + + g_object_notify (G_OBJECT (odevice), "description"); + return TRUE; + } + return FALSE; +} + +static gboolean +read_mixer_devices (OssDevice *device) +{ + gint devmask, + stereomask, + recmask; + gint ret; + gint i; + OssStreamControl *ctl; + + ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_DEVMASK), &devmask); + if (ret < 0) + goto fail; + ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_STEREODEVS), &stereomask); + if (ret < 0) + goto fail; + ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_RECMASK), &recmask); + if (ret < 0) + goto fail; + + for (i = 0; i < MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES); i++) { + gboolean stereo; + gboolean input = FALSE; + MateMixerPort *port = NULL; + + /* Skip unavailable controls */ + if ((devmask & (1 << i)) == 0) + continue; + + if ((stereomask & (1 << i)) > 0) + stereo = TRUE; + else + stereo = FALSE; + + if (oss_devices[i].flags == MATE_MIXER_STREAM_NO_FLAGS) { + if ((recmask & (1 << i)) > 0) + input = TRUE; + } + if (oss_devices[i].flags == MATE_MIXER_STREAM_INPUT) { + if ((recmask & (1 << i)) == 0) { + g_debug ("Skipping non-input device %s", oss_devices[i].name); + continue; + } + input = TRUE; + } + + ctl = oss_stream_control_new (device->priv->fd, + i, + oss_devices[i].name, + oss_devices[i].description, + stereo); + + if (oss_devices[i].role == MATE_MIXER_STREAM_CONTROL_ROLE_PORT) + port = _mate_mixer_port_new (oss_devices[i].name, + oss_devices[i].description, + NULL, + 0, + 0); + + if (input == TRUE) { + oss_stream_add_control (OSS_STREAM (device->priv->input), ctl); + + if (i == SOUND_MIXER_RECLEV || i == SOUND_MIXER_IGAIN) { + if (i == SOUND_MIXER_RECLEV) { + oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); + } else { + MateMixerStreamControl *defctl; + + defctl = mate_mixer_stream_get_default_control (device->priv->input); + if (defctl == NULL) + oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); + } + } + + if (port != NULL) + oss_stream_add_port (OSS_STREAM (device->priv->input), port); + } else { + oss_stream_add_control (OSS_STREAM (device->priv->output), ctl); + + if (i == SOUND_MIXER_VOLUME) { + oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); + } + else if (i == SOUND_MIXER_PCM) { + MateMixerStreamControl *defctl; + + defctl = mate_mixer_stream_get_default_control (device->priv->output); + if (defctl == NULL) + oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); + } + + if (port != NULL) + oss_stream_add_port (OSS_STREAM (device->priv->output), port); + } + + if (port != NULL) + oss_stream_control_set_port (ctl, port); + + oss_stream_control_set_role (ctl, oss_devices[i].role); + + g_debug ("Added control %s", + mate_mixer_stream_control_get_description (MATE_MIXER_STREAM_CONTROL (ctl))); + + oss_stream_control_update (ctl); + } + return TRUE; + +fail: + g_warning ("Failed to read device %s: %s", + device->priv->path, + g_strerror (errno)); + + return FALSE; +} + +static const gchar * +oss_device_get_name (MateMixerDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + + return OSS_DEVICE (device)->priv->name; +} + +static const gchar * +oss_device_get_description (MateMixerDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + + return OSS_DEVICE (device)->priv->description; +} + +static const gchar * +oss_device_get_icon (MateMixerDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + + return OSS_DEVICE_ICON; +} diff --git a/backends/oss/oss-device.h b/backends/oss/oss-device.h new file mode 100644 index 0000000..fe40eb2 --- /dev/null +++ b/backends/oss/oss-device.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef OSS_DEVICE_H +#define OSS_DEVICE_H + +#include +#include + +G_BEGIN_DECLS + +#define OSS_TYPE_DEVICE \ + (oss_device_get_type ()) +#define OSS_DEVICE(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS_TYPE_DEVICE, OssDevice)) +#define OSS_IS_DEVICE(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS_TYPE_DEVICE)) +#define OSS_DEVICE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), OSS_TYPE_DEVICE, OssDeviceClass)) +#define OSS_IS_DEVICE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), OSS_TYPE_DEVICE)) +#define OSS_DEVICE_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_DEVICE, OssDeviceClass)) + +typedef struct _OssDevice OssDevice; +typedef struct _OssDeviceClass OssDeviceClass; +typedef struct _OssDevicePrivate OssDevicePrivate; + +struct _OssDevice +{ + GObject parent; + + /*< private >*/ + OssDevicePrivate *priv; +}; + +struct _OssDeviceClass +{ + GObjectClass parent; +}; + +GType oss_device_get_type (void) G_GNUC_CONST; + +OssDevice * oss_device_new (const gchar *path, + gint fd); + +gboolean oss_device_read (OssDevice *device); + +const gchar * oss_device_get_path (OssDevice *odevice); + +MateMixerStream *oss_device_get_input_stream (OssDevice *odevice); +MateMixerStream *oss_device_get_output_stream (OssDevice *odevice); + +gboolean oss_device_set_description (OssDevice *odevice, + const gchar *description); + +G_END_DECLS + +#endif /* OSS_DEVICE_H */ diff --git a/backends/oss/oss-stream-control.c b/backends/oss/oss-stream-control.c new file mode 100644 index 0000000..0b1db26 --- /dev/null +++ b/backends/oss/oss-stream-control.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "oss-common.h" +#include "oss-stream-control.h" + +struct _OssStreamControlPrivate +{ + gint fd; + gint dev_number; + gchar *name; + gchar *description; + 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_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 *octl); +static void oss_stream_control_finalize (GObject *object); + +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 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 *ctl); + +static gboolean oss_stream_control_set_volume (MateMixerStreamControl *ctl, + guint volume); + +static guint oss_stream_control_get_channel_volume (MateMixerStreamControl *ctl, + 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, + guint channel); +static gboolean oss_stream_control_has_channel_position (MateMixerStreamControl *ctl, + MateMixerChannelPosition position); + +static gboolean oss_stream_control_set_balance (MateMixerStreamControl *ctl, + 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 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 void +oss_stream_control_class_init (OssStreamControlClass *klass) +{ + GObjectClass *object_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"); + + 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) +{ + 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); +} + +static void +oss_stream_control_finalize (GObject *object) +{ + OssStreamControl *octl; + + octl = OSS_STREAM_CONTROL (object); + + g_free (octl->priv->name); + g_free (octl->priv->description); + + if (octl->priv->fd != -1) + g_close (octl->priv->fd, NULL); + + 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) +{ + 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); + + return ctl; +} + +gboolean +oss_stream_control_update (OssStreamControl *octl) +{ + gint v; + gint ret; + + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + + ret = ioctl (octl->priv->fd, MIXER_READ (octl->priv->dev_number), &v); + if (ret < 0) { + g_warning ("Failed to read volume: %s", g_strerror (errno)); + return FALSE; + } + + octl->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); + + // XXX provide property + + if (octl->priv->port != NULL) + g_object_unref (octl->priv->port); + + octl->priv->port = g_object_ref (port); + return TRUE; +} + +gboolean +oss_stream_control_set_role (OssStreamControl *octl, MateMixerStreamControlRole role) +{ + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + + octl->priv->role = role; + return TRUE; +} + +static const gchar * +oss_stream_control_get_name (MateMixerStreamControl *ctl) +{ + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), NULL); + + return OSS_STREAM_CONTROL (ctl)->priv->name; +} + +static const gchar * +oss_stream_control_get_description (MateMixerStreamControl *ctl) +{ + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), NULL); + + return OSS_STREAM_CONTROL (ctl)->priv->description; +} + +static guint +oss_stream_control_get_num_channels (MateMixerStreamControl *ctl) +{ + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), 0); + + if (OSS_STREAM_CONTROL (ctl)->priv->stereo == TRUE) + return 2; + else + return 1; +} + +static gboolean +oss_stream_control_set_volume (MateMixerStreamControl *ctl, guint volume) +{ + OssStreamControl *octl; + int v; + int ret; + + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + + octl = OSS_STREAM_CONTROL (ctl); + + /* Some backends may allow setting higher value than maximum, but not here */ + if (volume > 100) + volume = 100; + + v = volume; + if (octl->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; +} + +static guint +oss_stream_control_get_channel_volume (MateMixerStreamControl *ctl, guint channel) +{ + OssStreamControl *octl; + + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), 0); + + octl = OSS_STREAM_CONTROL (ctl); + + if (channel > 1) + return 0; + + /* Right channel on mono stream will always have zero volume */ + return octl->priv->volume[channel]; +} + +static gboolean +oss_stream_control_set_channel_volume (MateMixerStreamControl *ctl, + guint channel, + guint volume) +{ + OssStreamControl *octl; + int ret; + int v; + + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), 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]; + + if (channel == 1) { + if (octl->priv->stereo == FALSE) + return FALSE; + + v |= volume << 8; + } else + v |= octl->priv->volume[1] << 8; + + 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; + } + return TRUE; +} + +static MateMixerChannelPosition +oss_stream_control_get_channel_position (MateMixerStreamControl *ctl, guint channel) +{ + OssStreamControl *octl; + + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), MATE_MIXER_CHANNEL_UNKNOWN); + + octl = OSS_STREAM_CONTROL (ctl); + + if (octl->priv->stereo == TRUE) { + if (channel == 0) + return MATE_MIXER_CHANNEL_FRONT_LEFT; + else if (channel == 1) + return MATE_MIXER_CHANNEL_FRONT_RIGHT; + } else { + if (channel == 0) + return MATE_MIXER_CHANNEL_MONO; + } + return MATE_MIXER_CHANNEL_UNKNOWN; +} + +static gboolean +oss_stream_control_has_channel_position (MateMixerStreamControl *ctl, + MateMixerChannelPosition position) +{ + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + + if (position == MATE_MIXER_CHANNEL_MONO) + return OSS_STREAM_CONTROL (ctl)->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 FALSE; +} + +static gboolean +oss_stream_control_set_balance (MateMixerStreamControl *ctl, gfloat balance) +{ + OssStreamControl *octl; + + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + + octl = OSS_STREAM_CONTROL (ctl); + + if (octl->priv->stereo == FALSE) + return FALSE; + + // XXX + return TRUE; +} + +static guint +oss_stream_control_get_min_volume (MateMixerStreamControl *ctl) +{ + return 0; +} + +static guint +oss_stream_control_get_max_volume (MateMixerStreamControl *ctl) +{ + return 100; +} + +static guint +oss_stream_control_get_normal_volume (MateMixerStreamControl *ctl) +{ + return 100; +} + +static guint +oss_stream_control_get_base_volume (MateMixerStreamControl *ctl) +{ + return 100; +} diff --git a/backends/oss/oss-stream-control.h b/backends/oss/oss-stream-control.h new file mode 100644 index 0000000..420af48 --- /dev/null +++ b/backends/oss/oss-stream-control.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef OSS_STREAM_CONTROL_H +#define OSS_STREAM_CONTROL_H + +#include +#include + +G_BEGIN_DECLS + +#define OSS_TYPE_STREAM_CONTROL \ + (oss_stream_control_get_type ()) +#define OSS_STREAM_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS_TYPE_STREAM_CONTROL, OssStreamControl)) +#define OSS_IS_STREAM_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS_TYPE_STREAM_CONTROL)) +#define OSS_STREAM_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), OSS_TYPE_STREAM_CONTROL, OssStreamControlClass)) +#define OSS_IS_STREAM_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), OSS_TYPE_STREAM_CONTROL)) +#define OSS_STREAM_CONTROL_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_STREAM_CONTROL, OssStreamControlClass)) + +typedef struct _OssStreamControl OssStreamControl; +typedef struct _OssStreamControlClass OssStreamControlClass; +typedef struct _OssStreamControlPrivate OssStreamControlPrivate; + +struct _OssStreamControl +{ + GObject parent; + + /*< private >*/ + OssStreamControlPrivate *priv; +}; + +struct _OssStreamControlClass +{ + GObjectClass parent; +}; + +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); + +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); + +G_END_DECLS + +#endif /* OSS_STREAM_CONTROL_H */ diff --git a/backends/oss/oss-stream.c b/backends/oss/oss-stream.c new file mode 100644 index 0000000..69bfd06 --- /dev/null +++ b/backends/oss/oss-stream.c @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include +#include +#include + +#include "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, +}; + +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); + +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 void +oss_stream_class_init (OssStreamClass *klass) +{ + GObjectClass *object_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; + + 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"); + + g_type_class_add_private (object_class, sizeof (OssStreamPrivate)); +} + +static void +oss_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + 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; + } +} + +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); +} + +static void +oss_stream_dispose (GObject *object) +{ + OssStream *ostream; + + ostream = OSS_STREAM (object); + + g_hash_table_remove_all (ostream->priv->controls); + g_hash_table_remove_all (ostream->priv->ports); + + G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object); +} + +static void +oss_stream_finalize (GObject *object) +{ + OssStream *ostream; + + ostream = OSS_STREAM (object); + + g_free (ostream->priv->name); + g_free (ostream->priv->description); + + g_hash_table_destroy (ostream->priv->controls); + g_hash_table_destroy (ostream->priv->ports); + + G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object); +} + +OssStream * +oss_stream_new (const gchar *name, + const gchar *description, + 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; + + return stream; +} + +gboolean +oss_stream_add_control (OssStream *ostream, OssStreamControl *octl) +{ + const gchar *name; + + g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (octl)); + + g_hash_table_insert (ostream->priv->controls, + g_strdup (name), + octl); + return TRUE; +} + +gboolean +oss_stream_set_default_control (OssStream *ostream, OssStreamControl *octl) +{ + g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), 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; +} + +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); + + 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) +{ + g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return g_hash_table_lookup (OSS_STREAM (stream)->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) +{ + OssStream *ostream; + + g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); + + ostream = OSS_STREAM (stream); + + if (ostream->priv->controls_list == NULL) + ostream->priv->controls_list = g_hash_table_get_values (ostream->priv->controls); + + return ostream->priv->controls_list; +} + +static const GList * +oss_stream_list_ports (MateMixerStream *stream) +{ + OssStream *ostream; + + g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); + + ostream = OSS_STREAM (stream); + + if (ostream->priv->ports_list == NULL) + ostream->priv->ports_list = g_hash_table_get_values (ostream->priv->ports); + + return ostream->priv->ports_list; +} diff --git a/backends/oss/oss-stream.h b/backends/oss/oss-stream.h new file mode 100644 index 0000000..d6c2fb2 --- /dev/null +++ b/backends/oss/oss-stream.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef OSS_STREAM_H +#define OSS_STREAM_H + +#include +#include + +#include "oss-stream-control.h" + +G_BEGIN_DECLS + +#define OSS_TYPE_STREAM \ + (oss_stream_get_type ()) +#define OSS_STREAM(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS_TYPE_STREAM, OssStream)) +#define OSS_IS_STREAM(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS_TYPE_STREAM)) +#define OSS_STREAM_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), OSS_TYPE_STREAM, OssStreamClass)) +#define OSS_IS_STREAM_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), OSS_TYPE_STREAM)) +#define OSS_STREAM_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_STREAM, OssStreamClass)) + +typedef struct _OssStream OssStream; +typedef struct _OssStreamClass OssStreamClass; +typedef struct _OssStreamPrivate OssStreamPrivate; + +struct _OssStream +{ + GObject parent; + + /*< private >*/ + OssStreamPrivate *priv; +}; + +struct _OssStreamClass +{ + GObjectClass parent; +}; + +GType oss_stream_get_type (void) G_GNUC_CONST; + +OssStream * oss_stream_new (const gchar *name, + const gchar *description, + MateMixerStreamFlags flags); + +gboolean oss_stream_add_control (OssStream *stream, + OssStreamControl *ctl); + +gboolean oss_stream_set_default_control (OssStream *stream, + OssStreamControl *ctl); + +gboolean oss_stream_add_port (OssStream *ostream, + MateMixerPort *port); + +G_END_DECLS + +#endif /* OSS_STREAM_H */ diff --git a/configure.ac b/configure.ac index d9bffc6..65e06e0 100644 --- a/configure.ac +++ b/configure.ac @@ -67,6 +67,7 @@ PKG_CHECK_MODULES(GLIB, [ glib-2.0 >= $GLIB_REQUIRED_VERSION gobject-2.0 >= $GLIB_REQUIRED_VERSION gmodule-2.0 >= $GLIB_REQUIRED_VERSION + gio-2.0 >= $GLIB_REQUIRED_VERSION ]) GTK_DOC_CHECK([1.10], [--flavour no-tmpl]) @@ -131,6 +132,7 @@ if test "x$enable_oss" != "xno"; then if test "x$ac_cv_header_soundcard_h" = "xyes" -o \ "x$ac_cv_header_sys_soundcard_h" = "xyes" -o \ "x$ac_cv_header_machine_soundcard_h" = "xyes"; then + AC_CHECK_LIB([ossaudio], [_oss_ioctl], [OSS_LIBS="-lossaudio"]) have_oss=yes else have_oss=no @@ -151,6 +153,7 @@ AM_CONDITIONAL(HAVE_OSS, test "x$have_oss" = "xyes") AC_SUBST(HAVE_OSS) AC_SUBST(OSS_CFLAGS) +AC_SUBST(OSS_LIBS) # ======================================================================= # Compiler warnings diff --git a/examples/monitor.c b/examples/monitor.c index 3267b36..dca5105 100644 --- a/examples/monitor.c +++ b/examples/monitor.c @@ -89,7 +89,7 @@ create_role_string (MateMixerClientStreamRole role) } static gchar * -create_volume_bar (MateMixerStream *stream, double *percent) +create_volume_bar (MateMixerStreamControl *ctl, double *percent) { GString *string; gint64 volume; @@ -100,9 +100,9 @@ create_volume_bar (MateMixerStream *stream, double *percent) int length = 30; int stars; - volume = mate_mixer_stream_get_volume (stream); - volume_min = mate_mixer_stream_get_min_volume (stream); - volume_max = mate_mixer_stream_get_normal_volume (stream); + volume = mate_mixer_stream_control_get_volume (ctl); + volume_min = mate_mixer_stream_control_get_min_volume (ctl); + volume_max = mate_mixer_stream_control_get_normal_volume (ctl); string = g_string_new ("["); @@ -192,24 +192,21 @@ print_streams (void) streams = mate_mixer_control_list_streams (control); while (streams) { - MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); - MateMixerClientStream *client = NULL; - gchar *volume_bar; - gdouble volume; + MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); + MateMixerStreamControl *ctl; + MateMixerClientStream *client = NULL; + gchar *volume_bar; + gdouble volume; if (mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CLIENT) { /* The application-specific details are accessible through the client * interface, which all client streams implement */ client = MATE_MIXER_CLIENT_STREAM (stream); - - /* Ignore event streams */ - if (mate_mixer_client_stream_get_role (client) == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) { - streams = streams->next; - continue; - } } - volume_bar = create_volume_bar (stream, &volume); + ctl = mate_mixer_stream_get_default_control (stream); + + volume_bar = create_volume_bar (ctl, &volume); g_print ("Stream %s\n" " |-| Description : %s\n" @@ -222,10 +219,10 @@ print_streams (void) mate_mixer_stream_get_description (stream), volume_bar, volume, - mate_mixer_stream_get_mute (stream) ? "Yes" : "No", - mate_mixer_stream_get_num_channels (stream), - mate_mixer_stream_get_balance (stream), - mate_mixer_stream_get_fade (stream)); + mate_mixer_stream_control_get_mute (ctl) ? "Yes" : "No", + mate_mixer_stream_control_get_num_channels (ctl), + mate_mixer_stream_control_get_balance (ctl), + mate_mixer_stream_control_get_fade (ctl)); if (client != NULL) { MateMixerClientStreamFlags client_flags; @@ -275,17 +272,20 @@ print_cached_streams (void) while (streams) { MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); + MateMixerStreamControl *ctl; MateMixerClientStream *client; MateMixerClientStreamFlags client_flags; MateMixerClientStreamRole client_role; gchar *volume_bar; gdouble volume; - client = MATE_MIXER_CLIENT_STREAM (stream); + ctl = mate_mixer_stream_get_default_control (stream); + + client = MATE_MIXER_CLIENT_STREAM (stream); client_flags = mate_mixer_client_stream_get_flags (client); client_role = mate_mixer_client_stream_get_role (client); - volume_bar = create_volume_bar (stream, &volume); + volume_bar = create_volume_bar (ctl, &volume); g_print ("Cached stream %s\n" " |-| Role : %s\n" @@ -298,10 +298,10 @@ print_cached_streams (void) create_role_string (client_role), volume_bar, volume, - mate_mixer_stream_get_mute (stream) ? "Yes" : "No", - mate_mixer_stream_get_num_channels (stream), - mate_mixer_stream_get_balance (stream), - mate_mixer_stream_get_fade (stream)); + mate_mixer_stream_control_get_mute (ctl) ? "Yes" : "No", + mate_mixer_stream_control_get_num_channels (ctl), + mate_mixer_stream_control_get_balance (ctl), + mate_mixer_stream_control_get_fade (ctl)); if (client_flags & MATE_MIXER_CLIENT_STREAM_APPLICATION) { gchar *app = create_app_string (mate_mixer_client_stream_get_app_name (client), @@ -394,7 +394,7 @@ int main (int argc, char *argv[]) GError *error = NULL; GOptionEntry entries[] = { - { "backend", 'b', 0, G_OPTION_ARG_STRING, &backend, "Sound system to use (pulseaudio, null)", NULL }, + { "backend", 'b', 0, G_OPTION_ARG_STRING, &backend, "Sound system to use (pulseaudio, oss, oss4, null)", NULL }, { "server", 's', 0, G_OPTION_ARG_STRING, &server, "Sound server address", NULL }, { NULL } }; @@ -427,6 +427,10 @@ int main (int argc, char *argv[]) if (backend) { if (!strcmp (backend, "pulseaudio")) mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_PULSEAUDIO); + else if (!strcmp (backend, "oss")) + mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_OSS); + else if (!strcmp (backend, "oss4")) + mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_OSS4); else if (!strcmp (backend, "null")) mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_NULL); else diff --git a/libmatemixer/Makefile.am b/libmatemixer/Makefile.am index 8c219d4..9666684 100644 --- a/libmatemixer/Makefile.am +++ b/libmatemixer/Makefile.am @@ -17,6 +17,7 @@ libmatemixer_include_HEADERS = \ matemixer-enums.h \ matemixer-port.h \ matemixer-stream.h \ + matemixer-stream-control.h \ matemixer-version.h libmatemixer_la_CFLAGS = $(GLIB_CFLAGS) @@ -37,7 +38,8 @@ libmatemixer_la_SOURCES = \ matemixer-enum-types.h \ matemixer-port.c \ matemixer-port-private.h \ - matemixer-stream.c + matemixer-stream.c \ + matemixer-stream-control.c libmatemixer_la_LIBADD = $(GLIB_LIBS) diff --git a/libmatemixer/matemixer-backend.c b/libmatemixer/matemixer-backend.c index be5c704..e070090 100644 --- a/libmatemixer/matemixer-backend.c +++ b/libmatemixer/matemixer-backend.c @@ -176,10 +176,15 @@ mate_mixer_backend_close (MateMixerBackend *backend) MateMixerState mate_mixer_backend_get_state (MateMixerBackend *backend) { + MateMixerState state = MATE_MIXER_STATE_UNKNOWN; + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN); - /* Implementation required */ - return MATE_MIXER_BACKEND_GET_INTERFACE (backend)->get_state (backend); + g_object_get (G_OBJECT (backend), + "state", &state, + NULL); + + return state; } GList * @@ -230,16 +235,18 @@ mate_mixer_backend_list_cached_streams (MateMixerBackend *backend) MateMixerStream * mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend) { - MateMixerBackendInterface *iface; + MateMixerStream *stream = NULL; g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + g_object_get (G_OBJECT (stream), + "default-input", &stream, + NULL); - if (iface->get_default_input_stream) - return iface->get_default_input_stream (backend); + if (stream != NULL) + g_object_unref (stream); - return NULL; + return stream; } gboolean @@ -261,16 +268,18 @@ mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream * mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend) { - MateMixerBackendInterface *iface; + MateMixerStream *stream = NULL; g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + g_object_get (G_OBJECT (stream), + "default-output", &stream, + NULL); - if (iface->get_default_output_stream) - return iface->get_default_output_stream (backend); + if (stream != NULL) + g_object_unref (stream); - return NULL; + return stream; } gboolean diff --git a/libmatemixer/matemixer-enum-types.c b/libmatemixer/matemixer-enum-types.c index 339b673..50e3bf4 100644 --- a/libmatemixer/matemixer-enum-types.c +++ b/libmatemixer/matemixer-enum-types.c @@ -94,14 +94,7 @@ mate_mixer_stream_flags_get_type (void) { MATE_MIXER_STREAM_INPUT, "MATE_MIXER_STREAM_INPUT", "input" }, { MATE_MIXER_STREAM_OUTPUT, "MATE_MIXER_STREAM_OUTPUT", "output" }, { MATE_MIXER_STREAM_CLIENT, "MATE_MIXER_STREAM_CLIENT", "client" }, - { MATE_MIXER_STREAM_HAS_MUTE, "MATE_MIXER_STREAM_HAS_MUTE", "has-mute" }, - { MATE_MIXER_STREAM_HAS_VOLUME, "MATE_MIXER_STREAM_HAS_VOLUME", "has-volume" }, - { MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME, "MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME", "has-decibel-volume" }, - { MATE_MIXER_STREAM_HAS_FLAT_VOLUME, "MATE_MIXER_STREAM_HAS_FLAT_VOLUME", "has-flat-volume" }, { MATE_MIXER_STREAM_HAS_MONITOR, "MATE_MIXER_STREAM_HAS_MONITOR", "has-monitor" }, - { MATE_MIXER_STREAM_CAN_SET_VOLUME, "MATE_MIXER_STREAM_CAN_SET_VOLUME", "can-set-volume" }, - { MATE_MIXER_STREAM_CAN_BALANCE, "MATE_MIXER_STREAM_CAN_BALANCE", "can-balance" }, - { MATE_MIXER_STREAM_CAN_FADE, "MATE_MIXER_STREAM_CAN_FADE", "can-fade" }, { MATE_MIXER_STREAM_CAN_SUSPEND, "MATE_MIXER_STREAM_CAN_SUSPEND", "can-suspend" }, { 0, NULL, NULL } }; @@ -132,6 +125,30 @@ mate_mixer_stream_state_get_type (void) return etype; } +GType +mate_mixer_stream_control_flags_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GFlagsValue values[] = { + { MATE_MIXER_STREAM_CONTROL_NO_FLAGS, "MATE_MIXER_STREAM_CONTROL_NO_FLAGS", "no-flags" }, + { MATE_MIXER_STREAM_CONTROL_HAS_MUTE, "MATE_MIXER_STREAM_CONTROL_HAS_MUTE", "has-mute" }, + { MATE_MIXER_STREAM_CONTROL_HAS_VOLUME, "MATE_MIXER_STREAM_CONTROL_HAS_VOLUME", "has-volume" }, + { MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL_VOLUME, "MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL_VOLUME", "has-decibel-volume" }, + { MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME, "MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME", "has-flat-volume" }, + { MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME, "MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME", "can-set-volume" }, + { MATE_MIXER_STREAM_CONTROL_CAN_BALANCE, "MATE_MIXER_STREAM_CONTROL_CAN_BALANCE", "can-balance" }, + { MATE_MIXER_STREAM_CONTROL_CAN_FADE, "MATE_MIXER_STREAM_CONTROL_CAN_FADE", "can-fade" }, + { 0, NULL, NULL } + }; + etype = g_flags_register_static ( + g_intern_static_string ("MateMixerStreamControlFlags"), + values); + } + return etype; +} + GType mate_mixer_client_stream_flags_get_type (void) { diff --git a/libmatemixer/matemixer-enum-types.h b/libmatemixer/matemixer-enum-types.h index 03c1297..1dc13cc 100644 --- a/libmatemixer/matemixer-enum-types.h +++ b/libmatemixer/matemixer-enum-types.h @@ -43,6 +43,9 @@ GType mate_mixer_stream_flags_get_type (void) G_GNUC_CONST; #define MATE_MIXER_TYPE_STREAM_STATE (mate_mixer_stream_state_get_type ()) GType mate_mixer_stream_state_get_type (void) G_GNUC_CONST; +#define MATE_MIXER_TYPE_STREAM_CONTROL_FLAGS (mate_mixer_stream_control_flags_get_type ()) +GType mate_mixer_stream_control_flags_get_type (void) G_GNUC_CONST; + #define MATE_MIXER_TYPE_CLIENT_STREAM_FLAGS (mate_mixer_client_stream_flags_get_type ()) GType mate_mixer_client_stream_flags_get_type (void) G_GNUC_CONST; diff --git a/libmatemixer/matemixer-enums.h b/libmatemixer/matemixer-enums.h index 7e523bb..b99f4c5 100644 --- a/libmatemixer/matemixer-enums.h +++ b/libmatemixer/matemixer-enums.h @@ -48,6 +48,7 @@ typedef enum { * will be the first one to try unless you select a specific backend * to connect to. * @MATE_MIXER_BACKEND_OSS: + * @MATE_MIXER_BACKEND_OSS4: * @MATE_MIXER_BACKEND_NULL: * Fallback backend which never fails to initialize, but provides no * functionality. This backend has the lowest priority and will be used @@ -58,6 +59,7 @@ typedef enum { MATE_MIXER_BACKEND_UNKNOWN, MATE_MIXER_BACKEND_PULSEAUDIO, MATE_MIXER_BACKEND_OSS, + MATE_MIXER_BACKEND_OSS4, MATE_MIXER_BACKEND_NULL } MateMixerBackendType; @@ -81,14 +83,7 @@ typedef enum { /*< flags >*/ * @MATE_MIXER_STREAM_INPUT: * @MATE_MIXER_STREAM_OUTPUT: * @MATE_MIXER_STREAM_CLIENT: - * @MATE_MIXER_STREAM_HAS_MUTE: - * @MATE_MIXER_STREAM_HAS_VOLUME: - * @MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME: - * @MATE_MIXER_STREAM_HAS_FLAT_VOLUME: * @MATE_MIXER_STREAM_HAS_MONITOR: - * @MATE_MIXER_STREAM_CAN_SET_VOLUME: - * @MATE_MIXER_STREAM_CAN_BALANCE: - * @MATE_MIXER_STREAM_CAN_FADE: * @MATE_MIXER_STREAM_CAN_SUSPEND: */ typedef enum { /*< flags >*/ @@ -96,15 +91,10 @@ typedef enum { /*< flags >*/ MATE_MIXER_STREAM_INPUT = 1 << 0, MATE_MIXER_STREAM_OUTPUT = 1 << 1, MATE_MIXER_STREAM_CLIENT = 1 << 2, - MATE_MIXER_STREAM_HAS_MUTE = 1 << 3, - MATE_MIXER_STREAM_HAS_VOLUME = 1 << 4, - MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME = 1 << 5, - MATE_MIXER_STREAM_HAS_FLAT_VOLUME = 1 << 6, - MATE_MIXER_STREAM_HAS_MONITOR = 1 << 7, - MATE_MIXER_STREAM_CAN_SET_VOLUME = 1 << 8, - MATE_MIXER_STREAM_CAN_BALANCE = 1 << 9, - MATE_MIXER_STREAM_CAN_FADE = 1 << 10, - MATE_MIXER_STREAM_CAN_SUSPEND = 1 << 11 + MATE_MIXER_STREAM_HAS_MONITOR = 1 << 3, + MATE_MIXER_STREAM_CAN_SUSPEND = 1 << 4, + MATE_MIXER_STREAM_PORTS_FIXED = 1 << 5, + MATE_MIXER_STREAM_PORTS_EXCLUSIVE = 1 << 6, } MateMixerStreamFlags; /** @@ -121,6 +111,39 @@ typedef enum { MATE_MIXER_STREAM_STATE_SUSPENDED } MateMixerStreamState; +/** + * MateMixerStreamControlFlags: + * @MATE_MIXER_STREAM_CONTROL_NO_FLAGS: + * @MATE_MIXER_STREAM_CONTROL_HAS_MUTE: + * @MATE_MIXER_STREAM_CONTROL_HAS_VOLUME: + * @MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL_VOLUME: + * @MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME: + * @MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME: + * @MATE_MIXER_STREAM_CONTROL_CAN_BALANCE: + * @MATE_MIXER_STREAM_CONTROL_CAN_FADE: + */ +typedef enum { + MATE_MIXER_STREAM_CONTROL_NO_FLAGS = 0, + MATE_MIXER_STREAM_CONTROL_HAS_MUTE = 1 << 0, + MATE_MIXER_STREAM_CONTROL_HAS_VOLUME = 1 << 1, + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL_VOLUME = 1 << 2, + MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME = 1 << 3, + MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME = 1 << 4, + MATE_MIXER_STREAM_CONTROL_CAN_BALANCE = 1 << 5, + MATE_MIXER_STREAM_CONTROL_CAN_FADE = 1 << 6 +} MateMixerStreamControlFlags; + +typedef enum { + MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, + MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, + MATE_MIXER_STREAM_CONTROL_ROLE_PCM, + MATE_MIXER_STREAM_CONTROL_ROLE_BASS, + MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, + MATE_MIXER_STREAM_CONTROL_ROLE_CD, + MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, + MATE_MIXER_STREAM_CONTROL_ROLE_PORT +} MateMixerStreamControlRole; + /** * MateMixerClientStreamFlags: * @MATE_MIXER_CLIENT_STREAM_NO_FLAGS: diff --git a/libmatemixer/matemixer-stream-control.c b/libmatemixer/matemixer-stream-control.c new file mode 100644 index 0000000..19d35a1 --- /dev/null +++ b/libmatemixer/matemixer-stream-control.c @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include + +#include "matemixer-enums.h" +#include "matemixer-enum-types.h" +#include "matemixer-stream-control.h" + +/** + * SECTION:matemixer-stream-control + * @include: libmatemixer/matemixer.h + */ + +G_DEFINE_INTERFACE (MateMixerStreamControl, mate_mixer_stream_control, G_TYPE_OBJECT) + +static void +mate_mixer_stream_control_default_init (MateMixerStreamControlInterface *iface) +{ + g_object_interface_install_property (iface, + g_param_spec_string ("name", + "Name", + "Name of the stream control", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_string ("description", + "Description", + "Description of the stream control", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_flags ("flags", + "Flags", + "Capability flags of the stream control", + MATE_MIXER_TYPE_STREAM_CONTROL_FLAGS, + MATE_MIXER_STREAM_CONTROL_NO_FLAGS, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_boolean ("mute", + "Mute", + "Mute state of the stream control", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_uint ("volume", + "Volume", + "Volume of the stream control", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_float ("balance", + "Balance", + "Balance value of the stream control", + -1.0f, + 1.0f, + 0.0f, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_interface_install_property (iface, + g_param_spec_float ("fade", + "Fade", + "Fade value of the stream control", + -1.0f, + 1.0f, + 0.0f, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + +const gchar * +mate_mixer_stream_control_get_name (MateMixerStreamControl *ctl) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), NULL); + + /* Implementation required */ + return MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl)->get_name (ctl); +} + +const gchar * +mate_mixer_stream_control_get_description (MateMixerStreamControl *ctl) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), NULL); + + /* Implementation required */ + return MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl)->get_description (ctl); +} + +MateMixerStreamControlFlags +mate_mixer_stream_control_get_flags (MateMixerStreamControl *ctl) +{ + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), MATE_MIXER_STREAM_CONTROL_NO_FLAGS); + + g_object_get (G_OBJECT (ctl), + "flags", &flags, + NULL); + + return flags; +} + +gboolean +mate_mixer_stream_control_get_mute (MateMixerStreamControl *ctl) +{ + gboolean mute = FALSE; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + + g_object_get (G_OBJECT (ctl), + "mute", &mute, + NULL); + + return mute; +} + +gboolean +mate_mixer_stream_control_set_mute (MateMixerStreamControl *ctl, gboolean mute) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->set_mute != NULL) + return iface->set_mute (ctl, mute); + + return FALSE; +} + +guint +mate_mixer_stream_control_get_volume (MateMixerStreamControl *ctl) +{ + guint volume = 0; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + + g_object_get (G_OBJECT (ctl), + "volume", &volume, + NULL); + + return volume; +} + +gboolean +mate_mixer_stream_control_set_volume (MateMixerStreamControl *ctl, guint volume) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->set_volume != NULL) + return iface->set_volume (ctl, volume); + + return FALSE; +} + +gdouble +mate_mixer_stream_control_get_decibel (MateMixerStreamControl *ctl) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), -MATE_MIXER_INFINITY); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->get_decibel != NULL) + return iface->get_decibel (ctl); + + return -MATE_MIXER_INFINITY; +} + +gboolean +mate_mixer_stream_control_set_decibel (MateMixerStreamControl *ctl, gdouble decibel) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->set_decibel != NULL) + return iface->set_decibel (ctl, decibel); + + return FALSE; +} + +guint +mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *ctl) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->get_num_channels != NULL) + return iface->get_num_channels (ctl); + + return 0; +} + +MateMixerChannelPosition +mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *ctl, guint channel) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), MATE_MIXER_CHANNEL_UNKNOWN); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->get_channel_position != NULL) + return iface->get_channel_position (ctl, channel); + + return MATE_MIXER_CHANNEL_UNKNOWN; +} + +guint +mate_mixer_stream_control_get_channel_volume (MateMixerStreamControl *ctl, guint channel) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->get_channel_volume != NULL) + return iface->get_channel_volume (ctl, channel); + + return 0; +} + +gboolean +mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *ctl, + guint channel, + guint volume) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->set_channel_volume != NULL) + return iface->set_channel_volume (ctl, channel, volume); + + return FALSE; +} + +gdouble +mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *ctl, guint channel) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), -MATE_MIXER_INFINITY); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->get_channel_decibel != NULL) + return iface->get_channel_decibel (ctl, channel); + + return -MATE_MIXER_INFINITY; +} + +gboolean +mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *ctl, + guint channel, + gdouble decibel) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->set_channel_decibel != NULL) + return iface->set_channel_decibel (ctl, channel, decibel); + + return FALSE; +} + +gboolean +mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *ctl, + MateMixerChannelPosition position) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->has_channel_position != NULL) + return iface->has_channel_position (ctl, position); + + return FALSE; +} + +gfloat +mate_mixer_stream_control_get_balance (MateMixerStreamControl *ctl) +{ + gfloat balance = 0.0f; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0.0f); + + g_object_get (G_OBJECT (ctl), + "balance", &balance, + NULL); + + return 0.0f; +} + +gboolean +mate_mixer_stream_control_set_balance (MateMixerStreamControl *ctl, gfloat balance) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->set_balance != NULL) + return iface->set_balance (ctl, balance); + + return FALSE; +} + +gfloat +mate_mixer_stream_control_get_fade (MateMixerStreamControl *ctl) +{ + gfloat fade = 0.0f; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0.0f); + + g_object_get (G_OBJECT (ctl), + "fade", &fade, + NULL); + + return fade; +} + +gboolean +mate_mixer_stream_control_set_fade (MateMixerStreamControl *ctl, gfloat fade) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->set_fade != NULL) + return iface->set_fade (ctl, fade); + + return FALSE; +} + +guint +mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *ctl) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->get_min_volume != NULL) + return iface->get_min_volume (ctl); + + return 0; +} + +guint +mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *ctl) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->get_max_volume != NULL) + return iface->get_max_volume (ctl); + + return 0; +} + +guint +mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *ctl) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->get_normal_volume != NULL) + return iface->get_normal_volume (ctl); + + return 0; +} + +guint +mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *ctl) +{ + MateMixerStreamControlInterface *iface; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + + iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + + if (iface->get_base_volume != NULL) + return iface->get_base_volume (ctl); + + return 0; +} diff --git a/libmatemixer/matemixer-stream-control.h b/libmatemixer/matemixer-stream-control.h new file mode 100644 index 0000000..727ed54 --- /dev/null +++ b/libmatemixer/matemixer-stream-control.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_STREAM_CONTROL_H +#define MATEMIXER_STREAM_CONTROL_H + +#include +#include +#include + +#include + +G_BEGIN_DECLS + +#ifdef INFINITY +# define MATE_MIXER_INFINITY INFINITY +#else +# define MATE_MIXER_INFINITY G_MAXDOUBLE +#endif + +#define MATE_MIXER_TYPE_STREAM_CONTROL \ + (mate_mixer_stream_control_get_type ()) +#define MATE_MIXER_STREAM_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_STREAM_CONTROL, MateMixerStreamControl)) +#define MATE_MIXER_IS_STREAM_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_STREAM_CONTROL)) +#define MATE_MIXER_STREAM_CONTROL_GET_INTERFACE(o) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_STREAM_CONTROL, MateMixerStreamControlInterface)) + +typedef struct _MateMixerStreamControl MateMixerStreamControl; /* dummy object */ +typedef struct _MateMixerStreamControlInterface MateMixerStreamControlInterface; + +struct _MateMixerStreamControlInterface +{ + GTypeInterface parent_iface; + + /*< private >*/ + const gchar * (*get_name) (MateMixerStreamControl *ctl); + const gchar * (*get_description) (MateMixerStreamControl *ctl); + + gboolean (*set_mute) (MateMixerStreamControl *ctl, + gboolean mute); + + guint (*get_num_channels) (MateMixerStreamControl *ctl); + + gboolean (*set_volume) (MateMixerStreamControl *ctl, + guint volume); + + gdouble (*get_decibel) (MateMixerStreamControl *ctl); + gboolean (*set_decibel) (MateMixerStreamControl *ctl, + gdouble decibel); + + gboolean (*has_channel_position) (MateMixerStreamControl *ctl, + MateMixerChannelPosition position); + MateMixerChannelPosition (*get_channel_position) (MateMixerStreamControl *ctl, + guint channel); + + guint (*get_channel_volume) (MateMixerStreamControl *ctl, + guint channel); + gboolean (*set_channel_volume) (MateMixerStreamControl *ctl, + guint channel, + guint volume); + + gdouble (*get_channel_decibel) (MateMixerStreamControl *ctl, + guint channel); + gboolean (*set_channel_decibel) (MateMixerStreamControl *ctl, + guint channel, + gdouble decibel); + + gboolean (*set_balance) (MateMixerStreamControl *ctl, + gfloat balance); + + gboolean (*set_fade) (MateMixerStreamControl *ctl, + gfloat fade); + + guint (*get_min_volume) (MateMixerStreamControl *ctl); + guint (*get_max_volume) (MateMixerStreamControl *ctl); + guint (*get_normal_volume) (MateMixerStreamControl *ctl); + guint (*get_base_volume) (MateMixerStreamControl *ctl); +}; + +GType mate_mixer_stream_control_get_type (void) G_GNUC_CONST; + +const gchar * mate_mixer_stream_control_get_name (MateMixerStreamControl *ctl); +const gchar * mate_mixer_stream_control_get_description (MateMixerStreamControl *ctl); +MateMixerStreamControlFlags mate_mixer_stream_control_get_flags (MateMixerStreamControl *ctl); + +gboolean mate_mixer_stream_control_get_mute (MateMixerStreamControl *ctl); +gboolean mate_mixer_stream_control_set_mute (MateMixerStreamControl *ctl, + gboolean mute); + +guint mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *ctl); + +guint mate_mixer_stream_control_get_volume (MateMixerStreamControl *ctl); +gboolean mate_mixer_stream_control_set_volume (MateMixerStreamControl *ctl, + guint volume); + +gdouble mate_mixer_stream_control_get_decibel (MateMixerStreamControl *ctl); +gboolean mate_mixer_stream_control_set_decibel (MateMixerStreamControl *ctl, + gdouble decibel); + +gboolean mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *ctl, + MateMixerChannelPosition position); +MateMixerChannelPosition mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *ctl, + guint channel); + +guint mate_mixer_stream_control_get_channel_volume (MateMixerStreamControl *ctl, + guint channel); +gboolean mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *ctl, + guint channel, + guint volume); + +gdouble mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *ctl, + guint channel); +gboolean mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *ctl, + guint channel, + gdouble decibel); + +gfloat mate_mixer_stream_control_get_balance (MateMixerStreamControl *ctl); +gboolean mate_mixer_stream_control_set_balance (MateMixerStreamControl *ctl, + gfloat balance); + +gfloat mate_mixer_stream_control_get_fade (MateMixerStreamControl *ctl); +gboolean mate_mixer_stream_control_set_fade (MateMixerStreamControl *ctl, + gfloat fade); + +guint mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *ctl); +guint mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *ctl); +guint mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *ctl); +guint mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *ctl); + +G_END_DECLS + +#endif /* MATEMIXER_STREAM_CONTROL_H */ diff --git a/libmatemixer/matemixer-stream.c b/libmatemixer/matemixer-stream.c index 6bec2be..888fddb 100644 --- a/libmatemixer/matemixer-stream.c +++ b/libmatemixer/matemixer-stream.c @@ -83,44 +83,6 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_interface_install_property (iface, - g_param_spec_boolean ("mute", - "Mute", - "Mute state of the stream", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_uint ("volume", - "Volume", - "Volume of the stream", - 0, - G_MAXUINT, - 0, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_float ("balance", - "Balance", - "Balance value of the stream", - -1.0f, - 1.0f, - 0.0f, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_float ("fade", - "Fade", - "Fade value of the stream", - -1.0f, - 1.0f, - 0.0f, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - g_object_interface_install_property (iface, g_param_spec_object ("active-port", "Active port", @@ -145,327 +107,161 @@ mate_mixer_stream_default_init (MateMixerStreamInterface *iface) const gchar * mate_mixer_stream_get_name (MateMixerStream *stream) { + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); + + /* Implementation required */ return MATE_MIXER_STREAM_GET_INTERFACE (stream)->get_name (stream); } const gchar * mate_mixer_stream_get_description (MateMixerStream *stream) { - MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_description) - return iface->get_description (stream); - - return NULL; + /* Implementation required */ + return MATE_MIXER_STREAM_GET_INTERFACE (stream)->get_description (stream); } MateMixerDevice * mate_mixer_stream_get_device (MateMixerStream *stream) { - MateMixerStreamInterface *iface; + MateMixerDevice *device = NULL; g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + g_object_get (G_OBJECT (stream), + "device", &device, + NULL); - if (iface->get_device) - return iface->get_device (stream); + if (device != NULL) + g_object_unref (device); + + return device; - return NULL; } MateMixerStreamFlags mate_mixer_stream_get_flags (MateMixerStream *stream) { - MateMixerStreamInterface *iface; + MateMixerStreamFlags flags = MATE_MIXER_STREAM_NO_FLAGS; g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_flags) - return iface->get_flags (stream); + g_object_get (G_OBJECT (stream), + "flags", &flags, + NULL); - return MATE_MIXER_STREAM_NO_FLAGS; + return flags; } MateMixerStreamState mate_mixer_stream_get_state (MateMixerStream *stream) { - MateMixerStreamInterface *iface; + MateMixerStreamState state = MATE_MIXER_STREAM_STATE_UNKNOWN; g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_state) - return iface->get_state (stream); + g_object_get (G_OBJECT (stream), + "state", &state, + NULL); - return MATE_MIXER_STREAM_STATE_UNKNOWN; + return state; } -gboolean -mate_mixer_stream_get_mute (MateMixerStream *stream) +MateMixerStreamControl * +mate_mixer_stream_get_control (MateMixerStream *stream, const gchar *name) { - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_mute) - return iface->get_mute (stream); - - return FALSE; -} - -gboolean -mate_mixer_stream_set_mute (MateMixerStream *stream, gboolean mute) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->set_mute) - return iface->set_mute (stream, mute); - - return FALSE; -} - -guint -mate_mixer_stream_get_volume (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_volume) - return iface->get_volume (stream); - - return 0; -} - -gboolean -mate_mixer_stream_set_volume (MateMixerStream *stream, guint volume) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->set_volume) - return iface->set_volume (stream, volume); - - return FALSE; -} - -gdouble -mate_mixer_stream_get_decibel (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), -MATE_MIXER_INFINITY); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_decibel) - return iface->get_decibel (stream); - - return -MATE_MIXER_INFINITY; -} - -gboolean -mate_mixer_stream_set_decibel (MateMixerStream *stream, gdouble decibel) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->set_decibel) - return iface->set_decibel (stream, decibel); - - return FALSE; -} - -guint -mate_mixer_stream_get_num_channels (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_num_channels) - return iface->get_num_channels (stream); - - return 0; -} - -MateMixerChannelPosition -mate_mixer_stream_get_channel_position (MateMixerStream *stream, guint channel) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_channel_position) - return iface->get_channel_position (stream, channel); - - return MATE_MIXER_CHANNEL_UNKNOWN; -} - -guint -mate_mixer_stream_get_channel_volume (MateMixerStream *stream, guint channel) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_channel_volume) - return iface->get_channel_volume (stream, channel); - - return 0; -} - -gboolean -mate_mixer_stream_set_channel_volume (MateMixerStream *stream, - guint channel, - guint volume) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->set_channel_volume) - return iface->set_channel_volume (stream, channel, volume); - - return FALSE; -} - -gdouble -mate_mixer_stream_get_channel_decibel (MateMixerStream *stream, guint channel) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), -MATE_MIXER_INFINITY); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_channel_decibel) - return iface->get_channel_decibel (stream, channel); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - return -MATE_MIXER_INFINITY; + /* Implementation required */ + return MATE_MIXER_STREAM_GET_INTERFACE (stream)->get_control (stream, name); } -gboolean -mate_mixer_stream_set_channel_decibel (MateMixerStream *stream, - guint channel, - gdouble decibel) +MateMixerStreamControl * +mate_mixer_stream_get_default_control (MateMixerStream *stream) { - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->set_channel_decibel) - return iface->set_channel_decibel (stream, channel, decibel); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - return FALSE; + /* Implementation required */ + return MATE_MIXER_STREAM_GET_INTERFACE (stream)->get_default_control (stream); } -gboolean -mate_mixer_stream_has_channel_position (MateMixerStream *stream, - MateMixerChannelPosition position) +MateMixerPort * +mate_mixer_stream_get_port (MateMixerStream *stream, const gchar *name) { MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->has_channel_position) - return iface->has_channel_position (stream, position); + if (iface->get_port != NULL) + return iface->get_port (stream, name); return FALSE; } -gfloat -mate_mixer_stream_get_balance (MateMixerStream *stream) +MateMixerPort * +mate_mixer_stream_get_active_port (MateMixerStream *stream) { - MateMixerStreamInterface *iface; + MateMixerPort *port = NULL; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0.0f); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + g_object_get (G_OBJECT (stream), + "active-port", &port, + NULL); - if (iface->get_balance) - return iface->get_balance (stream); + if (port != NULL) + g_object_unref (port); - return 0.0f; + return port; } gboolean -mate_mixer_stream_set_balance (MateMixerStream *stream, gfloat balance) +mate_mixer_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) { MateMixerStreamInterface *iface; g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->set_balance) - return iface->set_balance (stream, balance); + if (iface->set_active_port != NULL) + return iface->set_active_port (stream, port); return FALSE; } -gfloat -mate_mixer_stream_get_fade (MateMixerStream *stream) +const GList * +mate_mixer_stream_list_controls (MateMixerStream *stream) { MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0.0f); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->get_fade) - return iface->get_fade (stream); + if (iface->list_controls != NULL) + return iface->list_controls (stream); - return 0.0f; + return NULL; } -gboolean -mate_mixer_stream_set_fade (MateMixerStream *stream, gfloat fade) +const GList * +mate_mixer_stream_list_ports (MateMixerStream *stream) { MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->set_fade) - return iface->set_fade (stream, fade); + if (iface->list_ports != NULL) + return iface->list_ports (stream); - return FALSE; + return NULL; } gboolean @@ -477,7 +273,7 @@ mate_mixer_stream_suspend (MateMixerStream *stream) iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->suspend) + if (iface->suspend != NULL) return iface->suspend (stream); return FALSE; @@ -492,57 +288,28 @@ mate_mixer_stream_resume (MateMixerStream *stream) iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->resume) + if (iface->resume != NULL) return iface->resume (stream); return FALSE; } gboolean -mate_mixer_stream_monitor_start (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->monitor_start) - return iface->monitor_start (stream); - - return FALSE; -} - -void -mate_mixer_stream_monitor_stop (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_if_fail (MATE_MIXER_IS_STREAM (stream)); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->monitor_stop) - iface->monitor_stop (stream); -} - -gboolean -mate_mixer_stream_monitor_is_running (MateMixerStream *stream) +mate_mixer_stream_monitor_get_enabled (MateMixerStream *stream) { - MateMixerStreamInterface *iface; + gboolean enabled = FALSE; g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->monitor_is_running) - return iface->monitor_is_running (stream); + g_object_get (G_OBJECT (stream), + "monitor-enabled", &enabled, + NULL); - return FALSE; + return enabled; } gboolean -mate_mixer_stream_monitor_set_name (MateMixerStream *stream, const gchar *name) +mate_mixer_stream_monitor_set_enabled (MateMixerStream *stream, gboolean enabled) { MateMixerStreamInterface *iface; @@ -550,29 +317,14 @@ mate_mixer_stream_monitor_set_name (MateMixerStream *stream, const gchar *name) iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->monitor_set_name) - return iface->monitor_set_name (stream, name); + if (iface->monitor_set_enabled != NULL) + return iface->monitor_set_enabled (stream, enabled); return FALSE; } -const GList * -mate_mixer_stream_list_ports (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->list_ports) - return iface->list_ports (stream); - - return NULL; -} - -MateMixerPort * -mate_mixer_stream_get_active_port (MateMixerStream *stream) +const gchar * +mate_mixer_stream_monitor_get_name (MateMixerStream *stream) { MateMixerStreamInterface *iface; @@ -580,84 +332,23 @@ mate_mixer_stream_get_active_port (MateMixerStream *stream) iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->get_active_port) - return iface->get_active_port (stream); + if (iface->monitor_get_name != NULL) + return iface->monitor_get_name (stream); - return NULL; + return FALSE; } gboolean -mate_mixer_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) +mate_mixer_stream_monitor_set_name (MateMixerStream *stream, const gchar *name) { MateMixerStreamInterface *iface; g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - if (iface->set_active_port) - return iface->set_active_port (stream, port); + if (iface->monitor_set_name != NULL) + return iface->monitor_set_name (stream, name); return FALSE; } - -guint -mate_mixer_stream_get_min_volume (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_min_volume) - return iface->get_min_volume (stream); - - return 0; -} - -guint -mate_mixer_stream_get_max_volume (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_max_volume) - return iface->get_max_volume (stream); - - return 0; -} - -guint -mate_mixer_stream_get_normal_volume (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_normal_volume) - return iface->get_normal_volume (stream); - - return 0; -} - -guint -mate_mixer_stream_get_base_volume (MateMixerStream *stream) -{ - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), 0); - - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->get_base_volume) - return iface->get_base_volume (stream); - - return 0; -} diff --git a/libmatemixer/matemixer-stream.h b/libmatemixer/matemixer-stream.h index e91a2a5..aecf40e 100644 --- a/libmatemixer/matemixer-stream.h +++ b/libmatemixer/matemixer-stream.h @@ -18,13 +18,13 @@ #ifndef MATEMIXER_STREAM_H #define MATEMIXER_STREAM_H -#include #include #include #include #include #include +#include G_BEGIN_DECLS @@ -51,131 +51,69 @@ struct _MateMixerStreamInterface GTypeInterface parent_iface; /*< private >*/ - /* Virtual table */ - const gchar * (*get_name) (MateMixerStream *stream); - const gchar * (*get_description) (MateMixerStream *stream); - MateMixerDevice * (*get_device) (MateMixerStream *stream); - MateMixerStreamFlags (*get_flags) (MateMixerStream *stream); - MateMixerStreamState (*get_state) (MateMixerStream *stream); - gboolean (*get_mute) (MateMixerStream *stream); - gboolean (*set_mute) (MateMixerStream *stream, - gboolean mute); - guint (*get_num_channels) (MateMixerStream *stream); - guint (*get_volume) (MateMixerStream *stream); - gboolean (*set_volume) (MateMixerStream *stream, - guint volume); - gdouble (*get_decibel) (MateMixerStream *stream); - gboolean (*set_decibel) (MateMixerStream *stream, - gdouble decibel); - MateMixerChannelPosition (*get_channel_position) (MateMixerStream *stream, - guint channel); - guint (*get_channel_volume) (MateMixerStream *stream, - guint channel); - gboolean (*set_channel_volume) (MateMixerStream *stream, - guint channel, - guint volume); - gdouble (*get_channel_decibel) (MateMixerStream *stream, - guint channel); - gboolean (*set_channel_decibel) (MateMixerStream *stream, - guint channel, - gdouble decibel); - gboolean (*has_channel_position) (MateMixerStream *stream, - MateMixerChannelPosition position); - gfloat (*get_balance) (MateMixerStream *stream); - gboolean (*set_balance) (MateMixerStream *stream, - gfloat balance); - gfloat (*get_fade) (MateMixerStream *stream); - gboolean (*set_fade) (MateMixerStream *stream, - gfloat fade); - gboolean (*suspend) (MateMixerStream *stream); - gboolean (*resume) (MateMixerStream *stream); - gboolean (*monitor_start) (MateMixerStream *stream); - void (*monitor_stop) (MateMixerStream *stream); - gboolean (*monitor_is_running) (MateMixerStream *stream); - gboolean (*monitor_set_name) (MateMixerStream *stream, - const gchar *name); - const GList * (*list_ports) (MateMixerStream *stream); - MateMixerPort * (*get_active_port) (MateMixerStream *stream); - gboolean (*set_active_port) (MateMixerStream *stream, - MateMixerPort *port); - guint (*get_min_volume) (MateMixerStream *stream); - guint (*get_max_volume) (MateMixerStream *stream); - guint (*get_normal_volume) (MateMixerStream *stream); - guint (*get_base_volume) (MateMixerStream *stream); + const gchar * (*get_name) (MateMixerStream *stream); + const gchar * (*get_description) (MateMixerStream *stream); - /* Signals */ - void (*monitor_value) (MateMixerStream *stream, - gdouble value); -}; - -GType mate_mixer_stream_get_type (void) G_GNUC_CONST; + MateMixerStreamControl *(*get_control) (MateMixerStream *stream, + const gchar *name); + MateMixerStreamControl *(*get_default_control) (MateMixerStream *stream); -const gchar * mate_mixer_stream_get_name (MateMixerStream *stream); -const gchar * mate_mixer_stream_get_description (MateMixerStream *stream); -MateMixerDevice * mate_mixer_stream_get_device (MateMixerStream *stream); -MateMixerStreamFlags mate_mixer_stream_get_flags (MateMixerStream *stream); -MateMixerStreamState mate_mixer_stream_get_state (MateMixerStream *stream); + MateMixerPort * (*get_port) (MateMixerStream *stream, + const gchar *name); -gboolean mate_mixer_stream_get_mute (MateMixerStream *stream); -gboolean mate_mixer_stream_set_mute (MateMixerStream *stream, - gboolean mute); + gboolean (*set_active_port) (MateMixerStream *stream, + MateMixerPort *port); -guint mate_mixer_stream_get_num_channels (MateMixerStream *stream); + const GList * (*list_controls) (MateMixerStream *stream); + const GList * (*list_ports) (MateMixerStream *stream); -guint mate_mixer_stream_get_volume (MateMixerStream *stream); -gboolean mate_mixer_stream_set_volume (MateMixerStream *stream, - guint volume); + gboolean (*suspend) (MateMixerStream *stream); + gboolean (*resume) (MateMixerStream *stream); -gdouble mate_mixer_stream_get_decibel (MateMixerStream *stream); -gboolean mate_mixer_stream_set_decibel (MateMixerStream *stream, - gdouble decibel); + gboolean (*monitor_set_enabled) (MateMixerStream *stream, + gboolean enabled); -MateMixerChannelPosition mate_mixer_stream_get_channel_position (MateMixerStream *stream, - guint channel); + const gchar * (*monitor_get_name) (MateMixerStream *stream); + gboolean (*monitor_set_name) (MateMixerStream *stream, + const gchar *name); -guint mate_mixer_stream_get_channel_volume (MateMixerStream *stream, - guint channel); -gboolean mate_mixer_stream_set_channel_volume (MateMixerStream *stream, - guint channel, - guint volume); - -gdouble mate_mixer_stream_get_channel_decibel (MateMixerStream *stream, - guint channel); -gboolean mate_mixer_stream_set_channel_decibel (MateMixerStream *stream, - guint channel, - gdouble decibel); + /* Signals */ + void (*monitor_value) (MateMixerStream *stream, + gdouble value); +}; -gboolean mate_mixer_stream_has_channel_position (MateMixerStream *stream, - MateMixerChannelPosition position); +GType mate_mixer_stream_get_type (void) G_GNUC_CONST; -gfloat mate_mixer_stream_get_balance (MateMixerStream *stream); -gboolean mate_mixer_stream_set_balance (MateMixerStream *stream, - gfloat balance); +const gchar * mate_mixer_stream_get_name (MateMixerStream *stream); +const gchar * mate_mixer_stream_get_description (MateMixerStream *stream); +MateMixerDevice * mate_mixer_stream_get_device (MateMixerStream *stream); +MateMixerStreamFlags mate_mixer_stream_get_flags (MateMixerStream *stream); +MateMixerStreamState mate_mixer_stream_get_state (MateMixerStream *stream); -gfloat mate_mixer_stream_get_fade (MateMixerStream *stream); -gboolean mate_mixer_stream_set_fade (MateMixerStream *stream, - gfloat fade); +MateMixerStreamControl *mate_mixer_stream_get_control (MateMixerStream *stream, + const gchar *name); +MateMixerStreamControl *mate_mixer_stream_get_default_control (MateMixerStream *stream); -gboolean mate_mixer_stream_suspend (MateMixerStream *stream); -gboolean mate_mixer_stream_resume (MateMixerStream *stream); +MateMixerPort * mate_mixer_stream_get_port (MateMixerStream *stream, + const gchar *name); -gboolean mate_mixer_stream_monitor_start (MateMixerStream *stream); -void mate_mixer_stream_monitor_stop (MateMixerStream *stream); +MateMixerPort * mate_mixer_stream_get_active_port (MateMixerStream *stream); +gboolean mate_mixer_stream_set_active_port (MateMixerStream *stream, + MateMixerPort *port); -gboolean mate_mixer_stream_monitor_is_running (MateMixerStream *stream); -gboolean mate_mixer_stream_monitor_set_name (MateMixerStream *stream, - const gchar *name); +const GList * mate_mixer_stream_list_controls (MateMixerStream *stream); +const GList * mate_mixer_stream_list_ports (MateMixerStream *stream); -const GList * mate_mixer_stream_list_ports (MateMixerStream *stream); +gboolean mate_mixer_stream_suspend (MateMixerStream *stream); +gboolean mate_mixer_stream_resume (MateMixerStream *stream); -MateMixerPort * mate_mixer_stream_get_active_port (MateMixerStream *stream); -gboolean mate_mixer_stream_set_active_port (MateMixerStream *stream, - MateMixerPort *port); +gboolean mate_mixer_stream_monitor_get_enabled (MateMixerStream *stream); +gboolean mate_mixer_stream_monitor_set_enabled (MateMixerStream *stream, + gboolean enabled); -guint mate_mixer_stream_get_min_volume (MateMixerStream *stream); -guint mate_mixer_stream_get_max_volume (MateMixerStream *stream); -guint mate_mixer_stream_get_normal_volume (MateMixerStream *stream); -guint mate_mixer_stream_get_base_volume (MateMixerStream *stream); +const gchar * mate_mixer_stream_monitor_get_name (MateMixerStream *stream); +gboolean mate_mixer_stream_monitor_set_name (MateMixerStream *stream, + const gchar *name); G_END_DECLS -- cgit v1.2.1 From e369a047e3a43bd149fbce0af475bdef853aaf1a Mon Sep 17 00:00:00 2001 From: Michal Ratajsky Date: Fri, 25 Jul 2014 23:59:10 +0200 Subject: Fix return value --- libmatemixer/matemixer-stream-control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmatemixer/matemixer-stream-control.c b/libmatemixer/matemixer-stream-control.c index 19d35a1..388308e 100644 --- a/libmatemixer/matemixer-stream-control.c +++ b/libmatemixer/matemixer-stream-control.c @@ -337,7 +337,7 @@ mate_mixer_stream_control_get_balance (MateMixerStreamControl *ctl) "balance", &balance, NULL); - return 0.0f; + return balance; } gboolean -- cgit v1.2.1 From 8436ec1641eee8868128755f6d1475230cea25e6 Mon Sep 17 00:00:00 2001 From: Michal Ratajsky Date: Sun, 27 Jul 2014 13:12:53 +0200 Subject: Beginning of OSS4 support --- backends/Makefile.am | 4 + backends/oss4/Makefile.am | 29 +++ backends/oss4/oss4-backend.c | 487 ++++++++++++++++++++++++++++++++++++++++ backends/oss4/oss4-backend.h | 62 +++++ backends/oss4/oss4-common.h | 38 ++++ backends/oss4/oss4-device.c | 328 +++++++++++++++++++++++++++ backends/oss4/oss4-device.h | 72 ++++++ configure.ac | 36 +++ libmatemixer/matemixer-device.c | 9 +- 9 files changed, 1062 insertions(+), 3 deletions(-) create mode 100644 backends/oss4/Makefile.am create mode 100644 backends/oss4/oss4-backend.c create mode 100644 backends/oss4/oss4-backend.h create mode 100644 backends/oss4/oss4-common.h create mode 100644 backends/oss4/oss4-device.c create mode 100644 backends/oss4/oss4-device.h diff --git a/backends/Makefile.am b/backends/Makefile.am index 8ca01bd..0cf8dd4 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -12,4 +12,8 @@ if HAVE_OSS SUBDIRS += oss endif +if HAVE_OSS4 +SUBDIRS += oss4 +endif + -include $(top_srcdir)/git.mk diff --git a/backends/oss4/Makefile.am b/backends/oss4/Makefile.am new file mode 100644 index 0000000..cca8723 --- /dev/null +++ b/backends/oss4/Makefile.am @@ -0,0 +1,29 @@ +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 new file mode 100644 index 0000000..bd79fdf --- /dev/null +++ b/backends/oss4/oss4-backend.c @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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: (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 new file mode 100644 index 0000000..bc89e72 --- /dev/null +++ b/backends/oss4/oss4-backend.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef OSS4_BACKEND_H +#define OSS4_BACKEND_H + +#include +#include + +#include + +#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-common.h b/backends/oss4/oss4-common.h new file mode 100644 index 0000000..fe55b2b --- /dev/null +++ b/backends/oss4/oss4-common.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef OSS4_COMMON_H +#define OSS4_COMMON_H + +#include "config.h" + +#include +#include +#include +#include + +#ifdef HAVE_SYS_SOUNDCARD_H +# include +#elif HAVE_SOUNDCARD_H +# include +#elif HAVE_MACHINE_SOUNDCARD_H +# include +#else +# error "No OSS4 header file present" +#endif + +#endif /* OSS4_COMMON_H */ diff --git a/backends/oss4/oss4-device.c b/backends/oss4/oss4-device.c new file mode 100644 index 0000000..b68648b --- /dev/null +++ b/backends/oss4/oss4-device.c @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "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 new file mode 100644 index 0000000..3ec72e7 --- /dev/null +++ b/backends/oss4/oss4-device.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef OSS4_DEVICE_H +#define OSS4_DEVICE_H + +#include +#include + +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 */ diff --git a/configure.ac b/configure.ac index 65e06e0..b618913 100644 --- a/configure.ac +++ b/configure.ac @@ -155,6 +155,40 @@ AC_SUBST(HAVE_OSS) AC_SUBST(OSS_CFLAGS) AC_SUBST(OSS_LIBS) +# ----------------------------------------------------------------------- +# OSS4 +# ----------------------------------------------------------------------- +AC_ARG_ENABLE([oss4], + AS_HELP_STRING([--enable-oss4], + [Enable OSS4 backend module @<:@default=no@:>@]), + enable_oss4=$enableval, enable_oss4=no) + +if test "x$enable_oss4" != "xno"; then + AC_CHECK_HEADERS([soundcard.h sys/soundcard.h machine/soundcard.h]) + if test "x$ac_cv_header_soundcard_h" = "xyes" -o \ + "x$ac_cv_header_sys_soundcard_h" = "xyes" -o \ + "x$ac_cv_header_machine_soundcard_h" = "xyes"; then + have_oss4=yes + else + have_oss4=no + fi + + if test "x$enable_oss4" = "xyes" -a "x$have_oss4" = "xno"; then + AC_MSG_ERROR([OSS4 support explicitly requested but dependencies not found]) + fi + + if test "x$have_oss4" = "xyes" ; then + AC_DEFINE(HAVE_OSS4, [], [Define if we have OSS support]) + fi +else + have_oss4=no +fi + +AM_CONDITIONAL(HAVE_OSS4, test "x$have_oss4" = "xyes") + +AC_SUBST(HAVE_OSS4) +AC_SUBST(OSS4_CFLAGS) + # ======================================================================= # Compiler warnings # ======================================================================= @@ -207,6 +241,7 @@ libmatemixer/Makefile backends/Makefile backends/null/Makefile backends/oss/Makefile +backends/oss4/Makefile backends/pulse/Makefile data/Makefile data/libmatemixer.pc @@ -232,5 +267,6 @@ echo " Build Null module: $have_null Build PulseAudio module: $have_pulseaudio Build OSS module: $have_oss + Build OSS4 module: $have_oss4 " diff --git a/libmatemixer/matemixer-device.c b/libmatemixer/matemixer-device.c index 0406709..87517d6 100644 --- a/libmatemixer/matemixer-device.c +++ b/libmatemixer/matemixer-device.c @@ -38,7 +38,8 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) "Name", "Name of the device", NULL, - G_PARAM_READABLE | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, @@ -46,7 +47,8 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) "Description", "Description of the device", NULL, - G_PARAM_READABLE | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, @@ -54,7 +56,8 @@ mate_mixer_device_default_init (MateMixerDeviceInterface *iface) "Icon", "Name of the sound device icon", NULL, - G_PARAM_READABLE | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, -- cgit v1.2.1 From 6c6d4239ddc807e922df3874654f99eea291aadb Mon Sep 17 00:00:00 2001 From: Michal Ratajsky Date: Tue, 12 Aug 2014 04:56:55 +0200 Subject: Add ALSA, improve OSS and remove OSS4 --- backends/Makefile.am | 8 +- backends/alsa/Makefile.am | 47 + backends/alsa/alsa-backend.c | 508 ++++++++ backends/alsa/alsa-backend.h | 62 + backends/alsa/alsa-constants.c | 72 ++ backends/alsa/alsa-constants.h | 35 + backends/alsa/alsa-device.c | 941 +++++++++++++++ backends/alsa/alsa-device.h | 74 ++ backends/alsa/alsa-element.c | 53 + backends/alsa/alsa-element.h | 61 + backends/alsa/alsa-stream-control.c | 739 ++++++++++++ backends/alsa/alsa-stream-control.h | 111 ++ backends/alsa/alsa-stream-input-control.c | 329 +++++ backends/alsa/alsa-stream-input-control.h | 64 + backends/alsa/alsa-stream-output-control.c | 329 +++++ backends/alsa/alsa-stream-output-control.h | 64 + backends/alsa/alsa-stream.c | 273 +++++ backends/alsa/alsa-stream.h | 88 ++ backends/alsa/alsa-switch-option.c | 74 ++ backends/alsa/alsa-switch-option.h | 68 ++ backends/alsa/alsa-switch.c | 227 ++++ backends/alsa/alsa-switch.h | 65 + backends/alsa/alsa-toggle.c | 219 ++++ backends/alsa/alsa-toggle.h | 74 ++ backends/oss/oss-backend.c | 699 ++++++----- backends/oss/oss-backend.h | 5 +- backends/oss/oss-common.h | 1 + backends/oss/oss-device.c | 609 ++++------ backends/oss/oss-device.h | 28 +- backends/oss/oss-stream-control.c | 527 +++----- backends/oss/oss-stream-control.h | 26 +- backends/oss/oss-stream.c | 266 ++-- backends/oss/oss-stream.h | 25 +- backends/oss4/Makefile.am | 29 - backends/oss4/oss4-backend.c | 487 -------- backends/oss4/oss4-backend.h | 62 - backends/oss4/oss4-common.h | 38 - backends/oss4/oss4-device.c | 328 ----- backends/oss4/oss4-device.h | 72 -- configure.ac | 76 +- examples/monitor.c | 268 +++-- libmatemixer/Makefile.am | 20 +- libmatemixer/matemixer-backend-module.c | 149 ++- libmatemixer/matemixer-backend-module.h | 18 +- libmatemixer/matemixer-backend-private.h | 40 + libmatemixer/matemixer-backend.c | 520 ++++++-- libmatemixer/matemixer-backend.h | 131 +- libmatemixer/matemixer-client-stream.c | 311 +++-- libmatemixer/matemixer-client-stream.h | 31 +- libmatemixer/matemixer-context.c | 1393 +++++++++++++++++++++ libmatemixer/matemixer-context.h | 130 ++ libmatemixer/matemixer-control.c | 1466 ----------------------- libmatemixer/matemixer-control.h | 138 --- libmatemixer/matemixer-device-profile-private.h | 28 +- libmatemixer/matemixer-device-profile.c | 66 +- libmatemixer/matemixer-device-profile.h | 5 +- libmatemixer/matemixer-device.c | 499 ++++++-- libmatemixer/matemixer-device.h | 63 +- libmatemixer/matemixer-enum-types.c | 26 +- libmatemixer/matemixer-enum-types.h | 3 + libmatemixer/matemixer-enums.h | 28 +- libmatemixer/matemixer-port-private.h | 45 - libmatemixer/matemixer-port.c | 358 ------ libmatemixer/matemixer-port.h | 68 -- libmatemixer/matemixer-private.h | 84 +- libmatemixer/matemixer-stream-control-private.h | 41 + libmatemixer/matemixer-stream-control.c | 646 ++++++---- libmatemixer/matemixer-stream-control.h | 158 +-- libmatemixer/matemixer-stream.c | 444 ++++--- libmatemixer/matemixer-stream.h | 71 +- libmatemixer/matemixer-switch-option-private.h | 33 + libmatemixer/matemixer-switch-option.c | 224 ++++ libmatemixer/matemixer-switch-option.h | 64 + libmatemixer/matemixer-switch-private.h | 31 + libmatemixer/matemixer-switch.c | 283 +++++ libmatemixer/matemixer-switch.h | 82 ++ libmatemixer/matemixer-toggle.c | 254 ++++ libmatemixer/matemixer-toggle.h | 68 ++ libmatemixer/matemixer-types.h | 35 + libmatemixer/matemixer.c | 45 +- libmatemixer/matemixer.h | 8 +- 81 files changed, 10567 insertions(+), 5671 deletions(-) create mode 100644 backends/alsa/Makefile.am create mode 100644 backends/alsa/alsa-backend.c create mode 100644 backends/alsa/alsa-backend.h create mode 100644 backends/alsa/alsa-constants.c create mode 100644 backends/alsa/alsa-constants.h create mode 100644 backends/alsa/alsa-device.c create mode 100644 backends/alsa/alsa-device.h create mode 100644 backends/alsa/alsa-element.c create mode 100644 backends/alsa/alsa-element.h create mode 100644 backends/alsa/alsa-stream-control.c create mode 100644 backends/alsa/alsa-stream-control.h create mode 100644 backends/alsa/alsa-stream-input-control.c create mode 100644 backends/alsa/alsa-stream-input-control.h create mode 100644 backends/alsa/alsa-stream-output-control.c create mode 100644 backends/alsa/alsa-stream-output-control.h create mode 100644 backends/alsa/alsa-stream.c create mode 100644 backends/alsa/alsa-stream.h create mode 100644 backends/alsa/alsa-switch-option.c create mode 100644 backends/alsa/alsa-switch-option.h create mode 100644 backends/alsa/alsa-switch.c create mode 100644 backends/alsa/alsa-switch.h create mode 100644 backends/alsa/alsa-toggle.c create mode 100644 backends/alsa/alsa-toggle.h delete mode 100644 backends/oss4/Makefile.am delete mode 100644 backends/oss4/oss4-backend.c delete mode 100644 backends/oss4/oss4-backend.h delete mode 100644 backends/oss4/oss4-common.h delete mode 100644 backends/oss4/oss4-device.c delete mode 100644 backends/oss4/oss4-device.h create mode 100644 libmatemixer/matemixer-backend-private.h create mode 100644 libmatemixer/matemixer-context.c create mode 100644 libmatemixer/matemixer-context.h delete mode 100644 libmatemixer/matemixer-control.c delete mode 100644 libmatemixer/matemixer-control.h delete mode 100644 libmatemixer/matemixer-port-private.h delete mode 100644 libmatemixer/matemixer-port.c delete mode 100644 libmatemixer/matemixer-port.h create mode 100644 libmatemixer/matemixer-stream-control-private.h create mode 100644 libmatemixer/matemixer-switch-option-private.h create mode 100644 libmatemixer/matemixer-switch-option.c create mode 100644 libmatemixer/matemixer-switch-option.h create mode 100644 libmatemixer/matemixer-switch-private.h create mode 100644 libmatemixer/matemixer-switch.c create mode 100644 libmatemixer/matemixer-switch.h create mode 100644 libmatemixer/matemixer-toggle.c create mode 100644 libmatemixer/matemixer-toggle.h create mode 100644 libmatemixer/matemixer-types.h 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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_BACKEND_H +#define ALSA_BACKEND_H + +#include +#include +#include +#include + +#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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include +#include + +#include "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/alsa/alsa-constants.h b/backends/alsa/alsa-constants.h new file mode 100644 index 0000000..81257c7 --- /dev/null +++ b/backends/alsa/alsa-constants.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef ALSA_CONSTANTS_H +#define ALSA_CONSTANTS_H + +#include +#include +#include + +typedef struct { + gchar *name; + gchar *label; + MateMixerStreamControlRole role; +} AlsaControlInfo; + +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 /* 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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_DEVICE_H +#define ALSA_DEVICE_H + +#include +#include + +#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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_ELEMENT_H +#define ALSA_ELEMENT_H + +#include +#include +#include + +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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_STREAM_CONTROL_H +#define ALSA_STREAM_CONTROL_H + +#include +#include +#include +#include + +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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_STREAM_INPUT_CONTROL_H +#define ALSA_STREAM_INPUT_CONTROL_H + +#include +#include +#include + +#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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_STREAM_OUTPUT_CONTROL_H +#define ALSA_STREAM_OUTPUT_CONTROL_H + +#include +#include +#include + +#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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_STREAM_H +#define ALSA_STREAM_H + +#include +#include +#include + +#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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_SWITCH_OPTION_H +#define ALSA_SWITCH_OPTION_H + +#include +#include +#include + +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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_SWITCH_H +#define ALSA_SWITCH_H + +#include +#include +#include + +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 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include +#include + +#include "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 + * + * 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 . + */ + +#ifndef ALSA_TOGGLE_H +#define ALSA_TOGGLE_H + +#include +#include +#include + +#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 #include #include -#include #include -#include #include -#include +#include -#include -#include -#include -#include +#include +#include #include "oss-backend.h" #include "oss-common.h" #include "oss-device.h" +#include "oss-stream.h" #define BACKEND_NAME "OSS" #define BACKEND_PRIORITY 9 -#define PATH_SNDSTAT "/dev/sndstat" +#if !defined(__linux__) && !defined(__NetBSD__) && !defined(__OpenBSD__) + /* At least on systems based on FreeBSD we will need to read device names + * from the sndstat file, but avoid even trying that on systems where this + * is not needed and the file is not present */ +#define OSS_PATH_SNDSTAT "/dev/sndstat" +#endif + +#define OSS_MAX_DEVICES 32 struct _OssBackendPrivate { - GFile *sndstat; - GFile *dev; - GFileMonitor *dev_monitor; - GHashTable *devices; - GHashTable *streams; - MateMixerStream *default_input; - MateMixerStream *default_output; - MateMixerState state; -}; - -enum { - PROP_0, - PROP_STATE, - PROP_DEFAULT_INPUT, - PROP_DEFAULT_OUTPUT + gchar *default_device; + GSource *timeout_source; + GHashTable *devices; }; -static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); - static void oss_backend_class_init (OssBackendClass *klass); static void oss_backend_class_finalize (OssBackendClass *klass); - -static void oss_backend_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); - static void oss_backend_init (OssBackend *oss); static void oss_backend_dispose (GObject *object); static void oss_backend_finalize (GObject *object); -G_DEFINE_DYNAMIC_TYPE_EXTENDED (OssBackend, oss_backend, - G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, - mate_mixer_backend_interface_init)) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_DYNAMIC_TYPE (OssBackend, oss_backend, MATE_MIXER_TYPE_BACKEND) +#pragma clang diagnostic pop + +static gboolean oss_backend_open (MateMixerBackend *backend); +static void oss_backend_close (MateMixerBackend *backend); +static GList * oss_backend_list_devices (MateMixerBackend *backend); +static GList * oss_backend_list_streams (MateMixerBackend *backend); -static gboolean oss_backend_open (MateMixerBackend *backend); -static void oss_backend_close (MateMixerBackend *backend); -static GList * oss_backend_list_devices (MateMixerBackend *backend); -static GList * oss_backend_list_streams (MateMixerBackend *backend); +static gboolean read_devices (OssBackend *oss); -static void change_state (OssBackend *oss, - MateMixerState state); +static gboolean read_device (OssBackend *oss, + const gchar *path, + gboolean *added); -static void on_dev_monitor_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - OssBackend *oss); +static gchar * read_device_label (OssBackend *oss, + const gchar *path, + gint fd); -static gboolean read_device (OssBackend *oss, - const gchar *path); +static gchar * read_device_label_sndstat (OssBackend *oss, + const gchar *sndstat, + const gchar *path, + guint index) G_GNUC_UNUSED; -static gchar * read_device_description (OssBackend *oss, - const gchar *path, - gint fd); -static gchar * read_device_sndstat_description (const gchar *path, - GFileInputStream *input); +static void add_device (OssBackend *oss, + OssDevice *device); +static void remove_device (OssBackend *oss, + OssDevice *device); -static void add_device (OssBackend *oss, - OssDevice *device); -static void remove_device (OssBackend *oss, - OssDevice *device); +static void remove_stream (OssBackend *oss, + const gchar *name); + +static void select_default_input_stream (OssBackend *oss); +static void select_default_output_stream (OssBackend *oss); static MateMixerBackendInfo info; @@ -123,65 +110,31 @@ const MateMixerBackendInfo *backend_module_get_info (void) return &info; } -static void -mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) -{ - iface->open = oss_backend_open; - iface->close = oss_backend_close; - iface->list_devices = oss_backend_list_devices; - iface->list_streams = oss_backend_list_streams; -} - static void oss_backend_class_init (OssBackendClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerBackendClass *backend_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = oss_backend_dispose; - object_class->finalize = oss_backend_finalize; - object_class->get_property = oss_backend_get_property; + object_class->dispose = oss_backend_dispose; + object_class->finalize = oss_backend_finalize; - g_object_class_override_property (object_class, PROP_STATE, "state"); - g_object_class_override_property (object_class, PROP_DEFAULT_INPUT, "default-input"); - g_object_class_override_property (object_class, PROP_DEFAULT_OUTPUT, "default-output"); + backend_class = MATE_MIXER_BACKEND_CLASS (klass); + backend_class->open = oss_backend_open; + backend_class->close = oss_backend_close; + backend_class->list_devices = oss_backend_list_devices; + backend_class->list_streams = oss_backend_list_streams; g_type_class_add_private (object_class, sizeof (OssBackendPrivate)); } -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE() */ static void oss_backend_class_finalize (OssBackendClass *klass) { } -static void -oss_backend_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - OssBackend *oss; - - oss = OSS_BACKEND (object); - - switch (param_id) { - case PROP_STATE: - g_value_set_enum (value, oss->priv->state); - break; - case PROP_DEFAULT_INPUT: - g_value_set_object (value, oss->priv->default_input); - break; - case PROP_DEFAULT_OUTPUT: - g_value_set_object (value, oss->priv->default_output); - break; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - static void oss_backend_init (OssBackend *oss) { @@ -193,17 +146,21 @@ oss_backend_init (OssBackend *oss) g_str_equal, g_free, g_object_unref); - - oss->priv->streams = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); } static void oss_backend_dispose (GObject *object) { - oss_backend_close (MATE_MIXER_BACKEND (object)); + MateMixerBackend *backend; + MateMixerState state; + + backend = MATE_MIXER_BACKEND (object); + + state = mate_mixer_backend_get_state (backend); + if (state != MATE_MIXER_STATE_IDLE) + oss_backend_close (backend); + + G_OBJECT_CLASS (oss_backend_parent_class)->dispose (object); } static void @@ -214,61 +171,34 @@ oss_backend_finalize (GObject *object) oss = OSS_BACKEND (object); g_hash_table_destroy (oss->priv->devices); - g_hash_table_destroy (oss->priv->streams); + + G_OBJECT_CLASS (oss_backend_parent_class)->finalize (object); } static gboolean oss_backend_open (MateMixerBackend *backend) { OssBackend *oss; - GError *error = NULL; - gint i; g_return_val_if_fail (OSS_IS_BACKEND (backend), FALSE); oss = OSS_BACKEND (backend); - /* Monitor changes on /dev to catch hot-(un)plugged devices */ - // XXX works on linux, doesn't on freebsd, what to do on netbsd/openbsd? - oss->priv->dev = g_file_new_for_path ("/dev"); - oss->priv->dev_monitor = g_file_monitor_directory (oss->priv->dev, - G_FILE_MONITOR_NONE, - NULL, - &error); - if (oss->priv->dev_monitor != NULL) { - g_signal_connect (G_OBJECT (oss->priv->dev_monitor), - "changed", - G_CALLBACK (on_dev_monitor_changed), - oss); - } else { - g_debug ("Failed to monitor /dev: %s", error->message); - g_error_free (error); - } - -#if !defined(__linux__) && !defined(__NetBSD__) && !defined(__OpenBSD__) - /* At least on systems based on FreeBSD we will need to read devices names - * from the sndstat file, but avoid even trying that on systems where this - * is not needed and the file is not present */ - oss->priv->sndstat = g_file_new_for_path (PATH_SNDSTAT); -#endif - - for (i = 0; i < 5; i++) { - /* According to the OSS documentation the mixer devices are - * /dev/mixer0 - /dev/mixer4, of course some systems create them - * dynamically but this approach should be good enough */ - gchar *path = g_strdup_printf ("/dev/mixer%i", i); - - if (read_device (oss, path) == FALSE && i == 0) { - /* For the first mixer device check also /dev/mixer, but it - * might be a symlink to a real mixer device */ - if (g_file_test ("/dev/mixer", G_FILE_TEST_IS_SYMLINK) == FALSE) - read_device (oss, "/dev/mixer"); - } - - g_free (path); - } - - change_state (oss, MATE_MIXER_STATE_READY); + /* Discover added or removed OSS devices every second */ + oss->priv->timeout_source = g_timeout_source_new_seconds (1); + g_source_set_callback (oss->priv->timeout_source, + (GSourceFunc) read_devices, + oss, + NULL); + g_source_attach (oss->priv->timeout_source, + g_main_context_get_thread_default ()); + + /* Read the initial list of devices so we have some starting point, there + * isn't really a way to detect errors here, failing to add a device may + * be a device-related problem so make the backend always open successfully */ + read_devices (oss); + + _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_READY); return TRUE; } @@ -281,15 +211,13 @@ oss_backend_close (MateMixerBackend *backend) oss = OSS_BACKEND (backend); - g_clear_object (&oss->priv->default_input); - g_clear_object (&oss->priv->default_output); - - g_hash_table_remove_all (oss->priv->streams); + g_source_destroy (oss->priv->timeout_source); g_hash_table_remove_all (oss->priv->devices); - g_clear_object (&oss->priv->dev_monitor); - g_clear_object (&oss->priv->dev); - g_clear_object (&oss->priv->sndstat); + g_free (oss->priv->default_device); + oss->priv->default_device = NULL; + + _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE); } static GList * @@ -299,306 +227,361 @@ oss_backend_list_devices (MateMixerBackend *backend) g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ + /* Convert the hash table to a linked list, this list is expected to be + * cached in the main library */ list = g_hash_table_get_values (OSS_BACKEND (backend)->priv->devices); - if (list != NULL) { + if (list != NULL) g_list_foreach (list, (GFunc) g_object_ref, NULL); - return list; - } - return NULL; + return list; } static GList * oss_backend_list_streams (MateMixerBackend *backend) { - GList *list; + OssBackend *oss; + GHashTableIter iter; + gpointer value; + GList *list = NULL; g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ - list = g_hash_table_get_values (OSS_BACKEND (backend)->priv->streams); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); + oss = OSS_BACKEND (backend); - return list; + /* We don't keep a list or hash table of all streams here, instead walk + * through the list of devices and create the list manually, each device + * has at most one input and one output stream */ + g_hash_table_iter_init (&iter, oss->priv->devices); + + while (g_hash_table_iter_next (&iter, NULL, &value)) { + OssDevice *device = OSS_DEVICE (value); + OssStream *stream; + + stream = oss_device_get_output_stream (device); + if (stream != NULL) + list = g_list_prepend (list, stream); + stream = oss_device_get_input_stream (device); + if (stream != NULL) + list = g_list_prepend (list, stream); } - return NULL; -} - -static void -change_state (OssBackend *oss, MateMixerState state) -{ - if (oss->priv->state == state) - return; - oss->priv->state = state; + if (list != NULL) + g_list_foreach (list, (GFunc) g_object_ref, NULL); - g_object_notify (G_OBJECT (oss), "state"); + return list; } -static void -on_dev_monitor_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - OssBackend *oss) +static gboolean +read_devices (OssBackend *oss) { - gchar *path; + gint i; + gboolean added = FALSE; - if (event_type != G_FILE_MONITOR_EVENT_CREATED && - event_type != G_FILE_MONITOR_EVENT_DELETED) - return; + for (i = 0; i < OSS_MAX_DEVICES; i++) { + gboolean added_current; + gchar *path = g_strdup_printf ("/dev/mixer%i", i); - path = g_file_get_path (file); + /* On recent FreeBSD both /dev/mixer and /dev/mixer0 point to the same + * mixer device, on NetBSD and OpenBSD /dev/mixer is a symlink to one + * of the real mixer device nodes, on Linux /dev/mixer is the first + * device and /dev/mixer1 is the second device. + * Handle all of these cases by trying /dev/mixer if /dev/mixer0 fails */ + if (read_device (oss, path, &added_current) == FALSE && i == 0) + read_device (oss, "/dev/mixer", &added_current); + + if (added_current) + added = TRUE; - /* Only handle creation and deletion of mixer devices */ - if (g_str_has_prefix (path, "/dev/mixer") == FALSE) { - g_free (path); - return; - } - if (strcmp (path, "/dev/mixer") == 0 && - g_file_test (path, G_FILE_TEST_IS_SYMLINK) == TRUE) { g_free (path); - return; } - if (event_type == G_FILE_MONITOR_EVENT_DELETED) { - OssDevice *device; - - device = g_hash_table_lookup (oss->priv->devices, path); - if (device != NULL) - remove_device (oss, device); - } else - read_device (oss, path); - - g_free (path); + /* If any card has been added, make sure we have the most suitable default + * input and output streams */ + if (added == TRUE) { + select_default_input_stream (oss); + select_default_output_stream (oss); + } + return G_SOURCE_CONTINUE; } static gboolean -read_device (OssBackend *oss, const gchar *path) +read_device (OssBackend *oss, const gchar *path, gboolean *added) { - OssDevice *device; - gint fd; - gboolean ret; - const gchar *description; + OssDevice *device; + gint fd; + gchar *bname; + gchar *label; - /* We assume that the name and capabilities of a device do not change */ device = g_hash_table_lookup (oss->priv->devices, path); - if (G_UNLIKELY (device != NULL)) { - g_debug ("Attempt to re-read already know device %s", path); - return TRUE; - } + *added = FALSE; fd = g_open (path, O_RDWR, 0); if (fd == -1) { if (errno != ENOENT && errno != ENXIO) g_debug ("%s: %s", path, g_strerror (errno)); + + if (device != NULL) + remove_device (oss, device); return FALSE; } - device = oss_device_new (path, fd); + /* Don't proceed if the device is already known, opening the device was + * still tested to be absolutely sure that the device is removed it case + * it has disappeared, but normally the device's polling facility should + * handle this by itself */ + if (device != NULL) { + close (fd); + return TRUE; + } - description = read_device_description (oss, path, fd); - if (description != NULL) - oss_device_set_description (device, description); - else - oss_device_set_description (device, _("Unknown device")); + bname = g_path_get_basename (path); + label = read_device_label (oss, path, fd); - /* Close the descriptor as the device should dup it if it intends - * to keep it */ - g_close (fd, NULL); + device = oss_device_new (bname, label, path, fd); + g_free (bname); + g_free (label); - ret = oss_device_read (device); - if (ret == TRUE) + close (fd); + + if ((*added = oss_device_open (device)) == TRUE) add_device (oss, device); g_object_unref (device); - return ret; + return *added; } static gchar * -read_device_description (OssBackend *oss, const gchar *path, gint fd) +read_device_label (OssBackend *oss, const gchar *path, gint fd) { - struct mixer_info info; + guint index; - if (ioctl (fd, SOUND_MIXER_INFO, &info) == 0) { - /* Prefer device name supplied by the system, but this fails on FreeBSD */ - return g_strdup (info.name); - } +#ifdef SOUND_MIXER_INFO + do { + struct mixer_info info; - /* Reading the sndstat file is a bit desperate, but seem to be the only - * way to determine the name of the sound card on FreeBSD, it also has an - * advantage in being able to find out the default one */ - if (oss->priv->sndstat != NULL) { - GError *error = NULL; - GFileInputStream *input = g_file_read (oss->priv->sndstat, NULL, &error); - - if (input == NULL) { - /* The file can only be open by one application, otherwise the call - * fails with EBUSY */ - if (error->code == G_IO_ERROR_BUSY) { - g_debug ("Delayed reading %s as it is busy", PATH_SNDSTAT); - // XXX use timeout and try again - } else { - if (error->code == G_IO_ERROR_NOT_FOUND) - g_debug ("Device description is unknown as %s does not exist", - PATH_SNDSTAT); - else - g_debug ("%s: %s", PATH_SNDSTAT, error->message); + /* Prefer device name supplied by the system, but this calls fails + * with EINVAL on FreeBSD */ + if (ioctl (fd, SOUND_MIXER_INFO, &info) == 0) + return g_strdup (info.name); + } while (0); +#endif - g_clear_object (&oss->priv->sndstat); - } - g_error_free (error); - } else - return read_device_sndstat_description (path, input); - } + index = (guint) g_ascii_strtoull (path + sizeof ("/dev/mixer") - 1, + NULL, + 10); +#ifdef OSS_PATH_SNDSTAT + /* If the ioctl doesn't succeed, assume that the mixer device number + * matches the pcm number in the sndstat file, this is a bit desperate, but + * it should be correct on FreeBSD */ + do { + gchar *label; + + label = read_device_label_sndstat (oss, OSS_PATH_SNDSTAT, path, index); + if (label != NULL) + return label; + } while (0); +#endif - return NULL; + return g_strdup_printf (_("OSS Mixer %d"), index); } static gchar * -read_device_sndstat_description (const gchar *path, GFileInputStream *input) +read_device_label_sndstat (OssBackend *oss, + const gchar *sndstat, + const gchar *path, + guint index) { - gchar *line; - gchar *prefix; - gchar *description = NULL; - GDataInputStream *stream; - - if (G_UNLIKELY (g_str_has_prefix (path, "/dev/mixer")) == FALSE) { - g_warn_if_reached (); + FILE *fp; + gchar *label = NULL; + gchar *prefix; + gchar line[512]; + + fp = g_fopen (sndstat, "r"); + if (fp == NULL) { + g_debug ("Failed to open %s: %s", sndstat, g_strerror (errno)); return NULL; } - /* We assume that the mixer device number matches the pcm number in the - * sndstat file, this is a bit desperate, but it seems to do the - * right thing in practice */ - prefix = g_strdup_printf ("pcm%u: ", - (guint) g_ascii_strtoull (path + sizeof ("/dev/mixer") - 1, - NULL, - 10)); - - stream = g_data_input_stream_new (G_INPUT_STREAM (input)); - - while (TRUE) { - GError *error = NULL; - gchar *p; - - line = g_data_input_stream_read_line (stream, NULL, NULL, &error); - if (line == NULL) { - if (error != NULL) { - g_warning ("%s: %s", path, error->message); - g_error_free (error); - } - break; - } + /* Example line: + * pcm0: (play) default */ + prefix = g_strdup_printf ("pcm%u: ", index); + + while (fgets (line, sizeof (line), fp) != NULL) { + gchar *p; if (g_str_has_prefix (line, prefix) == FALSE) continue; - /* Example line: - * pcm0: (play) default */ p = strchr (line, '<'); if (p != NULL && *p && *(++p)) { gchar *end = strchr (p, '>'); - if (end != NULL) - description = g_strndup (p, end - p); - } + if (end != NULL) { + label = g_strndup (p, end - p); - // XXX we can also read "default" at the end of the line - // XXX http://ashish.is.lostca.se/2011/05/23/default-sound-device-in-freebsd/ - if (g_str_has_suffix (line, "default") || - g_str_has_suffix (line, "default)")) - ; + /* Normally the default OSS device is /dev/dsp, but on FreeBSD + * /dev/dsp doesn't physically exist on the filesystem, but is + * managed by the kernel according to the user-settable default + * device, in sndstat the default card definition ends with the + * word "default" */ + if (g_str_has_suffix (line, "default")) { + g_free (oss->priv->default_device); - if (description != NULL) + oss->priv->default_device = g_strdup (path); + } + } else { + g_debug ("Failed to read sndstat line: %s", line); + } break; + } } - g_object_unref (stream); g_free (prefix); + fclose (fp); - return description; + return label; } static void add_device (OssBackend *oss, OssDevice *device) { - MateMixerStream *stream; + const gchar *name; + + name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); - /* Add device, file path is used as the key rather than the name, because - * the name is not known until an OssDevice instance is created */ g_hash_table_insert (oss->priv->devices, g_strdup (oss_device_get_path (device)), g_object_ref (device)); - g_signal_emit_by_name (G_OBJECT (oss), - "device-added", - mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); - - /* Add streams if they exist */ - stream = oss_device_get_input_stream (device); - if (stream != NULL) { - g_hash_table_insert (oss->priv->streams, - g_strdup (mate_mixer_stream_get_name (stream)), - g_object_ref (stream)); - - g_signal_emit_by_name (G_OBJECT (oss), - "stream-added", - mate_mixer_stream_get_name (stream)); - } + // XXX make device emit it when closed + g_signal_connect_swapped (G_OBJECT (device), + "stream-removed", + G_CALLBACK (remove_stream), + oss); - stream = oss_device_get_output_stream (device); - if (stream != NULL) { - g_hash_table_insert (oss->priv->streams, - g_strdup (mate_mixer_stream_get_name (stream)), - g_object_ref (stream)); + g_signal_emit_by_name (G_OBJECT (oss), "device-added", name); - g_signal_emit_by_name (G_OBJECT (oss), - "stream-added", - mate_mixer_stream_get_name (stream)); - } + oss_device_load (device); } static void remove_device (OssBackend *oss, OssDevice *device) +{ + const gchar *name; + const gchar *path; + + path = oss_device_get_path (device); + name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + + g_signal_handlers_disconnect_by_func (G_OBJECT (device), + G_CALLBACK (remove_stream), + oss); + + // XXX close the device and make it remove streams + g_hash_table_remove (oss->priv->devices, path); + + g_signal_emit_by_name (G_OBJECT (oss), + "device-removed", + name); +} + +static void +remove_stream (OssBackend *oss, const gchar *name) { MateMixerStream *stream; - const gchar *path; - /* Remove the device streams first as they are a part of the device */ - stream = oss_device_get_input_stream (device); - if (stream != NULL) { - const gchar *name = mate_mixer_stream_get_name (stream); + stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (oss)); + + // XXX see if the change happens after stream is removed or before + if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) + select_default_input_stream (oss); + + stream = mate_mixer_backend_get_default_output_stream (MATE_MIXER_BACKEND (oss)); + + if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) + select_default_output_stream (oss); +} - g_hash_table_remove (oss->priv->streams, name); - g_signal_emit_by_name (G_OBJECT (oss), - "stream-removed", - name); +static void +select_default_input_stream (OssBackend *oss) +{ + OssDevice *device = NULL; + OssStream *stream; + gint i; + + /* Always prefer stream in the "default" device */ + if (oss->priv->default_device != NULL) + device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device); + if (device != NULL) { + stream = oss_device_get_input_stream (device); + if (stream != NULL) { + _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + return; + } } - stream = oss_device_get_output_stream (device); - if (stream != NULL) { - const gchar *name = mate_mixer_stream_get_name (stream); + for (i = 0; i < OSS_MAX_DEVICES; i++) { + gchar *path = g_strdup_printf ("/dev/mixer%i", i); - g_hash_table_remove (oss->priv->streams, stream); - g_signal_emit_by_name (G_OBJECT (oss), - "stream-removed", - name); + device = g_hash_table_lookup (oss->priv->devices, path); + if (device == NULL && i == 0) + device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer"); + + if (device != NULL) { + stream = oss_device_get_input_stream (device); + if (stream != NULL) { + _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + g_free (path); + return; + } + } + g_free (path); } - path = oss_device_get_path (device); + /* In the worst case unset the default stream */ + _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), NULL); +} - /* Remove the device */ - g_object_ref (device); +static void +select_default_output_stream (OssBackend *oss) +{ + OssDevice *device = NULL; + OssStream *stream; + gint i; + + /* Always prefer stream in the "default" device */ + if (oss->priv->default_device != NULL) + device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device); + if (device != NULL) { + stream = oss_device_get_output_stream (device); + if (stream != NULL) { + _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + return; + } + } - g_hash_table_remove (oss->priv->devices, path); - g_signal_emit_by_name (G_OBJECT (oss), - "device-removed", - mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); + for (i = 0; i < OSS_MAX_DEVICES; i++) { + gchar *path = g_strdup_printf ("/dev/mixer%i", i); - g_object_unref (device); + device = g_hash_table_lookup (oss->priv->devices, path); + if (device == NULL && i == 0) + device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer"); + + if (device != NULL) { + stream = oss_device_get_output_stream (device); + if (stream != NULL) { + _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + g_free (path); + return; + } + } + g_free (path); + } + + /* In the worst case unset the default stream */ + _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), NULL); } diff --git a/backends/oss/oss-backend.h b/backends/oss/oss-backend.h index b766799..325b61c 100644 --- a/backends/oss/oss-backend.h +++ b/backends/oss/oss-backend.h @@ -21,6 +21,7 @@ #include #include +#include #include #define OSS_TYPE_BACKEND \ @@ -42,7 +43,7 @@ typedef struct _OssBackendPrivate OssBackendPrivate; struct _OssBackend { - GObject parent; + MateMixerBackend parent; /*< private >*/ OssBackendPrivate *priv; @@ -50,7 +51,7 @@ struct _OssBackend struct _OssBackendClass { - GObjectClass parent_class; + MateMixerBackendClass parent_class; }; GType oss_backend_get_type (void) G_GNUC_CONST; diff --git a/backends/oss/oss-common.h b/backends/oss/oss-common.h index 7251b86..28a138d 100644 --- a/backends/oss/oss-common.h +++ b/backends/oss/oss-common.h @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef HAVE_SYS_SOUNDCARD_H # include diff --git a/backends/oss/oss-device.c b/backends/oss/oss-device.c index f33ff57..cf51705 100644 --- a/backends/oss/oss-device.c +++ b/backends/oss/oss-device.c @@ -22,12 +22,8 @@ #include #include -#include -#include -#include -#include -#include -#include +#include +#include #include "oss-common.h" #include "oss-device.h" @@ -36,490 +32,373 @@ #define OSS_DEVICE_ICON "audio-card" +typedef enum +{ + OSS_DEV_ANY, + OSS_DEV_INPUT, + OSS_DEV_OUTPUT +} OssDevType; + typedef struct { gchar *name; - gchar *description; + gchar *label; MateMixerStreamControlRole role; - MateMixerStreamFlags flags; -} OssDeviceName; - -static const OssDeviceName const oss_devices[] = { - { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, MATE_MIXER_STREAM_OUTPUT }, - { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, MATE_MIXER_STREAM_OUTPUT }, - { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, MATE_MIXER_STREAM_OUTPUT }, - { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, - { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, MATE_MIXER_STREAM_OUTPUT }, - { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, MATE_MIXER_STREAM_OUTPUT }, - { "line", N_("Line-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, MATE_MIXER_STREAM_INPUT }, + OssDevType type; +} OssDev; + +static const OssDev oss_devices[] = { + { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_OUTPUT }, + { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, OSS_DEV_OUTPUT }, + { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, OSS_DEV_OUTPUT }, + { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }, + { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT }, + /* OSS manual says this should be the beeper, but Linux OSS seems to assign it to + * regular volume control */ + { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, OSS_DEV_OUTPUT }, + { "line", N_("Line-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, OSS_DEV_INPUT }, /* Recording monitor */ - { "mix", N_("Mixer"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, - { "pcm2", N_("PCM-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, MATE_MIXER_STREAM_OUTPUT }, + { "mix", N_("Mixer"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "pcm2", N_("PCM-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT }, /* Recording level (master input) */ - { "rec", N_("Record"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, MATE_MIXER_STREAM_INPUT }, - { "igain", N_("In-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, - { "ogain", N_("Out-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, - { "line1", N_("Line-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "line2", N_("Line-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "line3", N_("Line-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "dig1", N_("Digital-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, - { "dig2", N_("Digital-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, - { "dig3", N_("Digital-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_NO_FLAGS }, - { "phin", N_("Phone-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "phout", N_("Phone-out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_OUTPUT }, - { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_INPUT }, - { "monitor", N_("Monitor"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, - { "depth", N_("3D-depth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, - { "center", N_("3D-center"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, - { "midi", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT } + { "rec", N_("Record"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_INPUT }, + { "igain", N_("In-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }, + { "ogain", N_("Out-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "line1", N_("Line-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "line2", N_("Line-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "line3", N_("Line-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "dig1", N_("Digital-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY }, + { "dig2", N_("Digital-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY }, + { "dig3", N_("Digital-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY }, + { "phin", N_("Phone-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "phout", N_("Phone-out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_OUTPUT }, + { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, + { "monitor", N_("Monitor"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "depth", N_("3D-depth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "center", N_("3D-center"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, + { "midi", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT } }; +#define OSS_N_DEVICES MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES) + struct _OssDevicePrivate { - gint fd; - gchar *path; - gchar *name; - gchar *description; - MateMixerStream *input; - MateMixerStream *output; -}; - -enum { - PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_ICON, - PROP_ACTIVE_PROFILE, - PROP_PATH, - PROP_FD + gint fd; + gchar *path; + gint devmask; + gint stereodevs; + gint recmask; + gint recsrc; + OssStream *input; + OssStream *output; }; -static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); - static void oss_device_class_init (OssDeviceClass *klass); - -static void oss_device_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void oss_device_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); - static void oss_device_init (OssDevice *device); +static void oss_device_dispose (GObject *object); static void oss_device_finalize (GObject *object); -G_DEFINE_TYPE_WITH_CODE (OssDevice, oss_device, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE, - mate_mixer_device_interface_init)) +G_DEFINE_TYPE (OssDevice, oss_device, MATE_MIXER_TYPE_DEVICE) -static const gchar *oss_device_get_name (MateMixerDevice *device); -static const gchar *oss_device_get_description (MateMixerDevice *device); -static const gchar *oss_device_get_icon (MateMixerDevice *device); +static GList * oss_device_list_streams (MateMixerDevice *device); -static gboolean read_mixer_devices (OssDevice *device); +static gboolean read_mixer_devices (OssDevice *device); -static void -mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) -{ - iface->get_name = oss_device_get_name; - iface->get_description = oss_device_get_description; - iface->get_icon = oss_device_get_icon; -} +static gboolean set_stream_default_control (OssStream *stream, + OssStreamControl *control, + gboolean force); static void oss_device_class_init (OssDeviceClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerDeviceClass *device_class; object_class = G_OBJECT_CLASS (klass); - object_class->finalize = oss_device_finalize; - object_class->get_property = oss_device_get_property; - object_class->set_property = oss_device_set_property; - - g_object_class_install_property (object_class, - PROP_PATH, - g_param_spec_string ("path", - "Path", - "Path to the device", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_FD, - g_param_spec_int ("fd", - "File descriptor", - "File descriptor of the device", - G_MININT, - G_MAXINT, - -1, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_override_property (object_class, PROP_NAME, "name"); - g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); - g_object_class_override_property (object_class, PROP_ICON, "icon"); - g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile"); + object_class->dispose = oss_device_dispose; + object_class->finalize = oss_device_finalize; + + device_class = MATE_MIXER_DEVICE_CLASS (klass); + device_class->list_streams = oss_device_list_streams; g_type_class_add_private (object_class, sizeof (OssDevicePrivate)); } static void -oss_device_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) +oss_device_init (OssDevice *device) { - OssDevice *device; - - device = OSS_DEVICE (object); - - switch (param_id) { - case PROP_NAME: - g_value_set_string (value, device->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, device->priv->description); - break; - case PROP_ICON: - g_value_set_string (value, OSS_DEVICE_ICON); - break; - case PROP_ACTIVE_PROFILE: - /* Not supported */ - g_value_set_object (value, NULL); - break; - case PROP_PATH: - g_value_set_string (value, device->priv->path); - break; - case PROP_FD: - g_value_set_int (value, device->priv->fd); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } + device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, + OSS_TYPE_DEVICE, + OssDevicePrivate); } static void -oss_device_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) +oss_device_dispose (GObject *object) { OssDevice *device; device = OSS_DEVICE (object); - switch (param_id) { - case PROP_PATH: - /* Construct-only string */ - device->priv->path = g_strdup (g_value_get_string (value)); - break; - case PROP_FD: - device->priv->fd = dup (g_value_get_int (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} + g_clear_object (&device->priv->input); + g_clear_object (&device->priv->output); -static void -oss_device_init (OssDevice *device) -{ - device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, - OSS_TYPE_DEVICE, - OssDevicePrivate); + G_OBJECT_CLASS (oss_device_parent_class)->dispose (object); } static void oss_device_finalize (GObject *object) { - OssDevice *device; - - device = OSS_DEVICE (object); - - g_free (device->priv->name); - g_free (device->priv->description); + OssDevice *device = OSS_DEVICE (object); - if (device->priv->fd != -1) - g_close (device->priv->fd, NULL); + close (device->priv->fd); G_OBJECT_CLASS (oss_device_parent_class)->finalize (object); } OssDevice * -oss_device_new (const gchar *path, gint fd) +oss_device_new (const gchar *name, const gchar *label, const gchar *path, gint fd) { OssDevice *device; - gchar *basename; + gchar *stream_name; - g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); device = g_object_new (OSS_TYPE_DEVICE, - "path", path, - "fd", fd, + "name", name, + "label", label, + "icon", OSS_DEVICE_ICON, NULL); - basename = g_path_get_basename (path); + device->priv->fd = dup (fd); + device->priv->path = g_strdup (path); - device->priv->name = g_strdup_printf ("oss-%s", basename); - g_free (basename); + stream_name = g_strdup_printf ("oss-input-%s", name); + device->priv->input = oss_stream_new (stream_name, + MATE_MIXER_DEVICE (device), + MATE_MIXER_STREAM_INPUT); + g_free (stream_name); + + stream_name = g_strdup_printf ("oss-output-%s", name); + device->priv->output = oss_stream_new (stream_name, + MATE_MIXER_DEVICE (device), + MATE_MIXER_STREAM_OUTPUT); + g_free (stream_name); return device; } gboolean -oss_device_read (OssDevice *device) +oss_device_open (OssDevice *device) { - gchar *name; - gchar *basename; - MateMixerStreamControl *ctl; + gint ret; g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); - // XXX avoid calling this function repeatedly - - g_debug ("Querying device %s (%s)", + g_debug ("Opening device %s (%s)", device->priv->path, - device->priv->description); - - basename = g_path_get_basename (device->priv->path); - - name = g_strdup_printf ("oss-input-%s", basename); - device->priv->input = MATE_MIXER_STREAM (oss_stream_new (name, - device->priv->description, - MATE_MIXER_STREAM_INPUT)); - g_free (name); - - name = g_strdup_printf ("oss-output-%s", basename); - device->priv->output = MATE_MIXER_STREAM (oss_stream_new (name, - device->priv->description, - MATE_MIXER_STREAM_OUTPUT | - MATE_MIXER_STREAM_PORTS_FIXED)); - g_free (name); - - if (read_mixer_devices (device) == FALSE) - return FALSE; - - // XXX prefer active ports as default if there is no default - - ctl = mate_mixer_stream_get_default_control (device->priv->input); - if (ctl == NULL) { - const GList *list = mate_mixer_stream_list_controls (device->priv->input); - - if (list != NULL) { - ctl = MATE_MIXER_STREAM_CONTROL (list->data); - oss_stream_set_default_control (OSS_STREAM (device->priv->input), - OSS_STREAM_CONTROL (ctl)); - } else - g_clear_object (&device->priv->input); + mate_mixer_device_get_label (MATE_MIXER_DEVICE (device))); + + /* Read the essential information about the device, these values are not + * expected to change and will not be queried */ + ret = ioctl (device->priv->fd, + MIXER_READ (SOUND_MIXER_DEVMASK), + &device->priv->devmask); + if (ret != 0) + goto fail; + + ret = ioctl (device->priv->fd, + MIXER_READ (SOUND_MIXER_STEREODEVS), + &device->priv->stereodevs); + if (ret < 0) + goto fail; + + ret = ioctl (device->priv->fd, + MIXER_READ (SOUND_MIXER_RECMASK), + &device->priv->recmask); + if (ret < 0) + goto fail; + + /* The recording source mask may change at any time, here we just read + * the initial value */ + ret = ioctl (device->priv->fd, + MIXER_READ (SOUND_MIXER_RECSRC), + &device->priv->recsrc); + if (ret < 0) + goto fail; + + /* NOTE: Linux also supports SOUND_MIXER_OUTSRC and SOUND_MIXER_OUTMASK which + * inform about/enable input->output, we could potentially create toggles + * for these, but these constants are not defined on any BSD. */ + + return TRUE; + +fail: + g_warning ("Failed to read device %s: %s", + device->priv->path, + g_strerror (errno)); + + return FALSE; +} + +gboolean +oss_device_load (OssDevice *device) +{ + MateMixerStreamControl *control; + + g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); + + read_mixer_devices (device); + + control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->input)); + if (control == NULL) { + // XXX pick something } - if (ctl != NULL) + if (control != NULL) g_debug ("Default input stream control is %s", - mate_mixer_stream_control_get_description (ctl)); - - ctl = mate_mixer_stream_get_default_control (device->priv->output); - if (ctl == NULL) { - const GList *list = mate_mixer_stream_list_controls (device->priv->output); - - if (list != NULL) { - ctl = MATE_MIXER_STREAM_CONTROL (list->data); - oss_stream_set_default_control (OSS_STREAM (device->priv->output), - OSS_STREAM_CONTROL (ctl)); - } else - g_clear_object (&device->priv->output); + mate_mixer_stream_control_get_label (control)); + + control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->output)); + if (control == NULL) { + // XXX pick something } - if (ctl != NULL) + if (control != NULL) g_debug ("Default output stream control is %s", - mate_mixer_stream_control_get_description (ctl)); + mate_mixer_stream_control_get_label (control)); return TRUE; } +gint +oss_device_get_fd (OssDevice *device) +{ + g_return_val_if_fail (OSS_IS_DEVICE (device), -1); + + return device->priv->fd; +} + const gchar * -oss_device_get_path (OssDevice *odevice) +oss_device_get_path (OssDevice *device) { - g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - return odevice->priv->path; + return device->priv->path; } -MateMixerStream * -oss_device_get_input_stream (OssDevice *odevice) +OssStream * +oss_device_get_input_stream (OssDevice *device) { - g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - return odevice->priv->input; + return device->priv->input; } -MateMixerStream * -oss_device_get_output_stream (OssDevice *odevice) +OssStream * +oss_device_get_output_stream (OssDevice *device) { - g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - return odevice->priv->output; + return device->priv->output; } -gboolean -oss_device_set_description (OssDevice *odevice, const gchar *description) +static GList * +oss_device_list_streams (MateMixerDevice *mmd) { - g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); + OssDevice *device; + GList *list = NULL; - if (g_strcmp0 (odevice->priv->description, description) != 0) { - g_free (odevice->priv->description); + g_return_val_if_fail (OSS_IS_DEVICE (mmd), NULL); - odevice->priv->description = g_strdup (description); + device = OSS_DEVICE (mmd); - g_object_notify (G_OBJECT (odevice), "description"); - return TRUE; - } - return FALSE; + if (device->priv->output != NULL) + list = g_list_prepend (list, g_object_ref (device->priv->output)); + if (device->priv->input != NULL) + list = g_list_prepend (list, g_object_ref (device->priv->input)); + + return list; } +#define OSS_MASK_HAS_DEVICE(mask,i) ((gboolean) (((mask) & (1 << (i))) > 0)) + static gboolean read_mixer_devices (OssDevice *device) { - gint devmask, - stereomask, - recmask; - gint ret; - gint i; - OssStreamControl *ctl; - - ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_DEVMASK), &devmask); - if (ret < 0) - goto fail; - ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_STEREODEVS), &stereomask); - if (ret < 0) - goto fail; - ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_RECMASK), &recmask); - if (ret < 0) - goto fail; + gint i; - for (i = 0; i < MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES); i++) { - gboolean stereo; - gboolean input = FALSE; - MateMixerPort *port = NULL; + for (i = 0; i < OSS_N_DEVICES; i++) { + OssStreamControl *control; + gboolean input = FALSE; /* Skip unavailable controls */ - if ((devmask & (1 << i)) == 0) + if (OSS_MASK_HAS_DEVICE (device->priv->devmask, i) == FALSE) continue; - if ((stereomask & (1 << i)) > 0) - stereo = TRUE; - else - stereo = FALSE; - - if (oss_devices[i].flags == MATE_MIXER_STREAM_NO_FLAGS) { - if ((recmask & (1 << i)) > 0) - input = TRUE; + if (oss_devices[i].type == OSS_DEV_ANY) { + input = OSS_MASK_HAS_DEVICE (device->priv->recmask, i); } - if (oss_devices[i].flags == MATE_MIXER_STREAM_INPUT) { - if ((recmask & (1 << i)) == 0) { - g_debug ("Skipping non-input device %s", oss_devices[i].name); - continue; - } + else if (oss_devices[i].type == OSS_DEV_INPUT) { input = TRUE; } - ctl = oss_stream_control_new (device->priv->fd, - i, - oss_devices[i].name, - oss_devices[i].description, - stereo); - - if (oss_devices[i].role == MATE_MIXER_STREAM_CONTROL_ROLE_PORT) - port = _mate_mixer_port_new (oss_devices[i].name, - oss_devices[i].description, - NULL, - 0, - 0); + control = oss_stream_control_new (oss_devices[i].name, + oss_devices[i].label, + oss_devices[i].role, + device->priv->fd, + i, + OSS_MASK_HAS_DEVICE (device->priv->stereodevs, i)); if (input == TRUE) { - oss_stream_add_control (OSS_STREAM (device->priv->input), ctl); + oss_stream_add_control (OSS_STREAM (device->priv->input), control); if (i == SOUND_MIXER_RECLEV || i == SOUND_MIXER_IGAIN) { - if (i == SOUND_MIXER_RECLEV) { - oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); - } else { - MateMixerStreamControl *defctl; - - defctl = mate_mixer_stream_get_default_control (device->priv->input); - if (defctl == NULL) - oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); - } + if (i == SOUND_MIXER_RECLEV) + set_stream_default_control (OSS_STREAM (device->priv->input), + control, + TRUE); + else + set_stream_default_control (OSS_STREAM (device->priv->input), + control, + FALSE); } - - if (port != NULL) - oss_stream_add_port (OSS_STREAM (device->priv->input), port); } else { - oss_stream_add_control (OSS_STREAM (device->priv->output), ctl); - - if (i == SOUND_MIXER_VOLUME) { - oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); - } - else if (i == SOUND_MIXER_PCM) { - MateMixerStreamControl *defctl; - - defctl = mate_mixer_stream_get_default_control (device->priv->output); - if (defctl == NULL) - oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); + oss_stream_add_control (OSS_STREAM (device->priv->output), control); + + if (i == SOUND_MIXER_VOLUME || i == SOUND_MIXER_PCM) { + if (i == SOUND_MIXER_VOLUME) + set_stream_default_control (OSS_STREAM (device->priv->output), + control, + TRUE); + else + set_stream_default_control (OSS_STREAM (device->priv->output), + control, + FALSE); } - - if (port != NULL) - oss_stream_add_port (OSS_STREAM (device->priv->output), port); } - if (port != NULL) - oss_stream_control_set_port (ctl, port); - - oss_stream_control_set_role (ctl, oss_devices[i].role); - g_debug ("Added control %s", - mate_mixer_stream_control_get_description (MATE_MIXER_STREAM_CONTROL (ctl))); + mate_mixer_stream_control_get_label (MATE_MIXER_STREAM_CONTROL (control))); - oss_stream_control_update (ctl); + oss_stream_control_update (control); } return TRUE; - -fail: - g_warning ("Failed to read device %s: %s", - device->priv->path, - g_strerror (errno)); - - return FALSE; } -static const gchar * -oss_device_get_name (MateMixerDevice *device) -{ - g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - - return OSS_DEVICE (device)->priv->name; -} - -static const gchar * -oss_device_get_description (MateMixerDevice *device) -{ - g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - - return OSS_DEVICE (device)->priv->description; -} - -static const gchar * -oss_device_get_icon (MateMixerDevice *device) +static gboolean +set_stream_default_control (OssStream *stream, OssStreamControl *control, gboolean force) { - g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); + MateMixerStreamControl *current; - return OSS_DEVICE_ICON; + current = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)); + if (current == NULL || force == TRUE) { + oss_stream_set_default_control (stream, OSS_STREAM_CONTROL (control)); + return TRUE; + } + return FALSE; } diff --git a/backends/oss/oss-device.h b/backends/oss/oss-device.h index fe40eb2..261a884 100644 --- a/backends/oss/oss-device.h +++ b/backends/oss/oss-device.h @@ -20,6 +20,9 @@ #include #include +#include + +#include "oss-stream.h" G_BEGIN_DECLS @@ -42,7 +45,7 @@ typedef struct _OssDevicePrivate OssDevicePrivate; struct _OssDevice { - GObject parent; + MateMixerDevice parent; /*< private >*/ OssDevicePrivate *priv; @@ -50,23 +53,24 @@ struct _OssDevice struct _OssDeviceClass { - GObjectClass parent; + MateMixerDeviceClass parent; }; -GType oss_device_get_type (void) G_GNUC_CONST; - -OssDevice * oss_device_new (const gchar *path, - gint fd); +GType oss_device_get_type (void) G_GNUC_CONST; -gboolean oss_device_read (OssDevice *device); +OssDevice * oss_device_new (const gchar *name, + const gchar *label, + const gchar *path, + gint fd); -const gchar * oss_device_get_path (OssDevice *odevice); +gboolean oss_device_open (OssDevice *device); +gboolean oss_device_load (OssDevice *device); -MateMixerStream *oss_device_get_input_stream (OssDevice *odevice); -MateMixerStream *oss_device_get_output_stream (OssDevice *odevice); +gint oss_device_get_fd (OssDevice *device); +const gchar *oss_device_get_path (OssDevice *device); -gboolean oss_device_set_description (OssDevice *odevice, - const gchar *description); +OssStream * oss_device_get_input_stream (OssDevice *device); +OssStream * oss_device_get_output_stream (OssDevice *device); G_END_DECLS diff --git a/backends/oss/oss-stream-control.c b/backends/oss/oss-stream-control.c index 0b1db26..5161528 100644 --- a/backends/oss/oss-stream-control.c +++ b/backends/oss/oss-stream-control.c @@ -15,14 +15,13 @@ * License along with this library; if not, see . */ -#include #include #include #include #include -#include -#include +#include +#include #include "oss-common.h" #include "oss-stream-control.h" @@ -30,437 +29,279 @@ struct _OssStreamControlPrivate { gint fd; - gint dev_number; - gchar *name; - gchar *description; + gint devnum; guint volume[2]; gfloat balance; gboolean stereo; - MateMixerPort *port; MateMixerStreamControlRole role; MateMixerStreamControlFlags flags; }; -enum { - PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_FLAGS, - PROP_MUTE, - PROP_VOLUME, - PROP_BALANCE, - PROP_FADE, - PROP_FD, - PROP_DEV_NUMBER, - PROP_STEREO -}; - -static void mate_mixer_stream_control_interface_init (MateMixerStreamControlInterface *iface); - -static void oss_stream_control_class_init (OssStreamControlClass *klass); +static void oss_stream_control_class_init (OssStreamControlClass *klass); -static void oss_stream_control_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void oss_stream_control_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); +static void oss_stream_control_init (OssStreamControl *control); +static void oss_stream_control_finalize (GObject *object); -static void oss_stream_control_init (OssStreamControl *octl); -static void oss_stream_control_finalize (GObject *object); +G_DEFINE_TYPE (OssStreamControl, oss_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL) -G_DEFINE_TYPE_WITH_CODE (OssStreamControl, oss_stream_control, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM_CONTROL, - mate_mixer_stream_control_interface_init)) +static gboolean oss_stream_control_set_mute (MateMixerStreamControl *mmsc, + gboolean mute); -static const gchar * oss_stream_control_get_name (MateMixerStreamControl *ctl); -static const gchar * oss_stream_control_get_description (MateMixerStreamControl *ctl); +static guint oss_stream_control_get_num_channels (MateMixerStreamControl *mmsc); -static guint oss_stream_control_get_num_channels (MateMixerStreamControl *ctl); +static guint oss_stream_control_get_volume (MateMixerStreamControl *mmsc); -static gboolean oss_stream_control_set_volume (MateMixerStreamControl *ctl, +static gboolean oss_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume); -static guint oss_stream_control_get_channel_volume (MateMixerStreamControl *ctl, +static gboolean oss_stream_control_has_channel_position (MateMixerStreamControl *mmsc, + MateMixerChannelPosition position); +static MateMixerChannelPosition oss_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel); -static gboolean oss_stream_control_set_channel_volume (MateMixerStreamControl *ctl, - guint channel, - guint volume); -static MateMixerChannelPosition oss_stream_control_get_channel_position (MateMixerStreamControl *ctl, +static guint oss_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel); -static gboolean oss_stream_control_has_channel_position (MateMixerStreamControl *ctl, - MateMixerChannelPosition position); +static gboolean oss_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, + guint channel, + guint volume); -static gboolean oss_stream_control_set_balance (MateMixerStreamControl *ctl, +static gboolean oss_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance); -static guint oss_stream_control_get_min_volume (MateMixerStreamControl *ctl); -static guint oss_stream_control_get_max_volume (MateMixerStreamControl *ctl); -static guint oss_stream_control_get_normal_volume (MateMixerStreamControl *ctl); -static guint oss_stream_control_get_base_volume (MateMixerStreamControl *ctl); +static guint oss_stream_control_get_min_volume (MateMixerStreamControl *mmsc); +static guint oss_stream_control_get_max_volume (MateMixerStreamControl *mmsc); +static guint oss_stream_control_get_normal_volume (MateMixerStreamControl *mmsc); +static guint oss_stream_control_get_base_volume (MateMixerStreamControl *mmsc); -static void -mate_mixer_stream_control_interface_init (MateMixerStreamControlInterface *iface) -{ - iface->get_name = oss_stream_control_get_name; - iface->get_description = oss_stream_control_get_description; - iface->get_num_channels = oss_stream_control_get_num_channels; - iface->set_volume = oss_stream_control_set_volume; - iface->get_channel_volume = oss_stream_control_get_channel_volume; - iface->set_channel_volume = oss_stream_control_set_channel_volume; - iface->get_channel_position = oss_stream_control_get_channel_position; - iface->has_channel_position = oss_stream_control_has_channel_position; - iface->set_balance = oss_stream_control_set_balance; - iface->get_min_volume = oss_stream_control_get_min_volume; - iface->get_max_volume = oss_stream_control_get_max_volume; - iface->get_normal_volume = oss_stream_control_get_normal_volume; - iface->get_base_volume = oss_stream_control_get_base_volume; -} +static gboolean write_volume (OssStreamControl *control, + gint volume); static void oss_stream_control_class_init (OssStreamControlClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerStreamControlClass *control_class; object_class = G_OBJECT_CLASS (klass); - object_class->finalize = oss_stream_control_finalize; - object_class->get_property = oss_stream_control_get_property; - object_class->set_property = oss_stream_control_set_property; - - g_object_class_install_property (object_class, - PROP_FD, - g_param_spec_int ("fd", - "File descriptor", - "File descriptor of the device", - G_MININT, - G_MAXINT, - -1, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_DEV_NUMBER, - g_param_spec_int ("dev-number", - "Dev number", - "OSS device number", - G_MININT, - G_MAXINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_STEREO, - g_param_spec_boolean ("stereo", - "Stereo", - "Stereo", - FALSE, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_override_property (object_class, PROP_NAME, "name"); - g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); - g_object_class_override_property (object_class, PROP_FLAGS, "flags"); - g_object_class_override_property (object_class, PROP_MUTE, "mute"); - g_object_class_override_property (object_class, PROP_VOLUME, "volume"); - g_object_class_override_property (object_class, PROP_BALANCE, "balance"); - g_object_class_override_property (object_class, PROP_FADE, "fade"); + object_class->finalize = oss_stream_control_finalize; + + control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = oss_stream_control_set_mute; + control_class->get_num_channels = oss_stream_control_get_num_channels; + control_class->get_volume = oss_stream_control_get_volume; + control_class->set_volume = oss_stream_control_set_volume; + control_class->get_channel_volume = oss_stream_control_get_channel_volume; + control_class->set_channel_volume = oss_stream_control_set_channel_volume; + control_class->get_channel_position = oss_stream_control_get_channel_position; + control_class->has_channel_position = oss_stream_control_has_channel_position; + control_class->set_balance = oss_stream_control_set_balance; + control_class->get_min_volume = oss_stream_control_get_min_volume; + control_class->get_max_volume = oss_stream_control_get_max_volume; + control_class->get_normal_volume = oss_stream_control_get_normal_volume; + control_class->get_base_volume = oss_stream_control_get_base_volume; g_type_class_add_private (object_class, sizeof (OssStreamControlPrivate)); } static void -oss_stream_control_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - OssStreamControl *octl; - - octl = OSS_STREAM_CONTROL (object); - - switch (param_id) { - case PROP_NAME: - g_value_set_string (value, octl->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, octl->priv->description); - break; - case PROP_FLAGS: - g_value_set_flags (value, octl->priv->flags); - break; - case PROP_MUTE: - /* Not supported */ - g_value_set_boolean (value, FALSE); - break; - case PROP_VOLUME: - g_value_set_uint (value, MAX (octl->priv->volume[0], octl->priv->volume[1])); - break; - case PROP_BALANCE: - g_value_set_float (value, octl->priv->balance); - break; - case PROP_FADE: - /* Not supported */ - g_value_set_float (value, 0.0f); - break; - case PROP_FD: - g_value_set_int (value, octl->priv->fd); - break; - case PROP_DEV_NUMBER: - g_value_set_int (value, octl->priv->dev_number); - break; - case PROP_STEREO: - g_value_set_boolean (value, octl->priv->stereo); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -oss_stream_control_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) +oss_stream_control_init (OssStreamControl *control) { - OssStreamControl *octl; - - octl = OSS_STREAM_CONTROL (object); - - switch (param_id) { - case PROP_FD: - octl->priv->fd = dup (g_value_get_int (value)); - break; - case PROP_DEV_NUMBER: - octl->priv->dev_number = g_value_get_int (value); - break; - case PROP_STEREO: - octl->priv->stereo = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -oss_stream_control_init (OssStreamControl *octl) -{ - octl->priv = G_TYPE_INSTANCE_GET_PRIVATE (octl, - OSS_TYPE_STREAM_CONTROL, - OssStreamControlPrivate); + control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, + OSS_TYPE_STREAM_CONTROL, + OssStreamControlPrivate); } static void oss_stream_control_finalize (GObject *object) { - OssStreamControl *octl; - - octl = OSS_STREAM_CONTROL (object); + OssStreamControl *control; - g_free (octl->priv->name); - g_free (octl->priv->description); + control = OSS_STREAM_CONTROL (object); - if (octl->priv->fd != -1) - g_close (octl->priv->fd, NULL); + close (control->priv->fd); G_OBJECT_CLASS (oss_stream_control_parent_class)->finalize (object); } OssStreamControl * -oss_stream_control_new (gint fd, - gint dev_number, - const gchar *name, - const gchar *description, - gboolean stereo) +oss_stream_control_new (const gchar *name, + const gchar *label, + MateMixerStreamControlRole role, + gint fd, + gint devnum, + gboolean stereo) { - OssStreamControl *ctl; - - ctl = g_object_new (OSS_TYPE_STREAM_CONTROL, - "fd", fd, - "dev-number", dev_number, - "stereo", stereo, - NULL); - - ctl->priv->name = g_strdup (name); - ctl->priv->description = g_strdup (description); + OssStreamControl *control; + MateMixerStreamControlFlags flags; - return ctl; + flags = MATE_MIXER_STREAM_CONTROL_HAS_VOLUME | + MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME; + if (stereo == TRUE) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + + control = g_object_new (OSS_TYPE_STREAM_CONTROL, + "name", name, + "label", label, + "flags", flags, + NULL); + + control->priv->fd = fd; + control->priv->devnum = devnum; + control->priv->stereo = stereo; + return control; } gboolean -oss_stream_control_update (OssStreamControl *octl) +oss_stream_control_update (OssStreamControl *control) { gint v; gint ret; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); - ret = ioctl (octl->priv->fd, MIXER_READ (octl->priv->dev_number), &v); + ret = ioctl (control->priv->fd, MIXER_READ (control->priv->devnum), &v); if (ret < 0) { g_warning ("Failed to read volume: %s", g_strerror (errno)); return FALSE; } - octl->priv->volume[0] = v & 0xFF; + control->priv->volume[0] = v & 0xFF; - if (octl->priv->stereo == TRUE) - octl->priv->volume[1] = (v >> 8) & 0xFF; - - return TRUE; -} - -gboolean -oss_stream_control_set_port (OssStreamControl *octl, MateMixerPort *port) -{ - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + if (control->priv->stereo == TRUE) { + gfloat balance; + guint left; + guint right; - // XXX provide property + control->priv->volume[1] = (v >> 8) & 0xFF; - if (octl->priv->port != NULL) - g_object_unref (octl->priv->port); + /* Calculate balance */ + left = control->priv->volume[0]; + right = control->priv->volume[1]; + if (left == right) + balance = 0.0f; + else if (left > right) + balance = -1.0f + ((gfloat) right / (gfloat) left); + else + balance = +1.0f - ((gfloat) left / (gfloat) right); - octl->priv->port = g_object_ref (port); + _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), + balance); + } return TRUE; } -gboolean -oss_stream_control_set_role (OssStreamControl *octl, MateMixerStreamControlRole role) +static gboolean +oss_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute) { - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); - octl->priv->role = role; + // TODO return TRUE; } -static const gchar * -oss_stream_control_get_name (MateMixerStreamControl *ctl) +static guint +oss_stream_control_get_num_channels (MateMixerStreamControl *mmsc) { - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), NULL); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), 0); - return OSS_STREAM_CONTROL (ctl)->priv->name; + return (OSS_STREAM_CONTROL (mmsc)->priv->stereo == TRUE) ? 2 : 1; } -static const gchar * -oss_stream_control_get_description (MateMixerStreamControl *ctl) +static guint +oss_stream_control_get_volume (MateMixerStreamControl *mmsc) { - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), NULL); + OssStreamControl *control; - return OSS_STREAM_CONTROL (ctl)->priv->description; -} + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), 0); -static guint -oss_stream_control_get_num_channels (MateMixerStreamControl *ctl) -{ - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), 0); + control = OSS_STREAM_CONTROL (mmsc); - if (OSS_STREAM_CONTROL (ctl)->priv->stereo == TRUE) - return 2; + if (control->priv->stereo == TRUE) + return MAX (control->priv->volume[0], control->priv->volume[1]); else - return 1; + return control->priv->volume[0]; } static gboolean -oss_stream_control_set_volume (MateMixerStreamControl *ctl, guint volume) +oss_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume) { - OssStreamControl *octl; - int v; - int ret; - - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + OssStreamControl *control; + gint v; - octl = OSS_STREAM_CONTROL (ctl); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); - /* Some backends may allow setting higher value than maximum, but not here */ - if (volume > 100) - volume = 100; + control = OSS_STREAM_CONTROL (mmsc); - v = volume; - if (octl->priv->stereo == TRUE) + v = CLAMP (volume, 0, 100); + if (control->priv->stereo == TRUE) v |= (volume & 0xFF) << 8; - ret = ioctl (octl->priv->fd, MIXER_WRITE (octl->priv->dev_number), &v); - if (ret < 0) { - g_warning ("Failed to set volume: %s", g_strerror (errno)); - return FALSE; - } - return TRUE; + return write_volume (control, v); } static guint -oss_stream_control_get_channel_volume (MateMixerStreamControl *ctl, guint channel) +oss_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel) { - OssStreamControl *octl; + OssStreamControl *control; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), 0); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), 0); - octl = OSS_STREAM_CONTROL (ctl); + control = OSS_STREAM_CONTROL (mmsc); - if (channel > 1) - return 0; - - /* Right channel on mono stream will always have zero volume */ - return octl->priv->volume[channel]; + if (control->priv->stereo == TRUE) { + if (channel == 0 || channel == 1) + return control->priv->volume[channel]; + } else { + if (channel == 0) + return control->priv->volume[0]; + } + return 0; } static gboolean -oss_stream_control_set_channel_volume (MateMixerStreamControl *ctl, +oss_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume) { - OssStreamControl *octl; - int ret; - int v; + OssStreamControl *control; + gint v; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); - if (channel > 1) - return FALSE; - - octl = OSS_STREAM_CONTROL (ctl); - - /* Some backends may allow setting higher value than maximum, but not here */ - if (volume > 100) - volume = 100; - - if (channel == 0) - v = volume; - else - v = octl->priv->volume[0]; + control = OSS_STREAM_CONTROL (mmsc); + volume = CLAMP (volume, 0, 100); - if (channel == 1) { - if (octl->priv->stereo == FALSE) + if (control->priv->stereo == TRUE) { + if (channel > 1) return FALSE; - v |= volume << 8; - } else - v |= octl->priv->volume[1] << 8; + /* Stereo channel volume - left channel is in the lowest 8 bits and + * right channel is in the higher 8 bits */ + if (channel == 0) + v = volume | (control->priv->volume[1] << 8); + else + v = control->priv->volume[0] | (volume << 8); + } else { + if (channel > 0) + return FALSE; - ret = ioctl (octl->priv->fd, MIXER_WRITE (octl->priv->dev_number), &v); - if (ret < 0) { - g_warning ("Failed to set channel volume: %s", g_strerror (errno)); - return FALSE; + /* Single channel volume - only lowest 8 bits are used */ + v = volume; } - return TRUE; + + return write_volume (control, v); } static MateMixerChannelPosition -oss_stream_control_get_channel_position (MateMixerStreamControl *ctl, guint channel) +oss_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel) { - OssStreamControl *octl; + OssStreamControl *control; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), MATE_MIXER_CHANNEL_UNKNOWN); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN); - octl = OSS_STREAM_CONTROL (ctl); + control = OSS_STREAM_CONTROL (mmsc); - if (octl->priv->stereo == TRUE) { + if (control->priv->stereo == TRUE) { if (channel == 0) return MATE_MIXER_CHANNEL_FRONT_LEFT; else if (channel == 1) @@ -473,57 +314,79 @@ oss_stream_control_get_channel_position (MateMixerStreamControl *ctl, guint chan } static gboolean -oss_stream_control_has_channel_position (MateMixerStreamControl *ctl, +oss_stream_control_has_channel_position (MateMixerStreamControl *mmsc, MateMixerChannelPosition position) { - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); if (position == MATE_MIXER_CHANNEL_MONO) - return OSS_STREAM_CONTROL (ctl)->priv->stereo == FALSE; + return OSS_STREAM_CONTROL (mmsc)->priv->stereo == FALSE; if (position == MATE_MIXER_CHANNEL_FRONT_LEFT || position == MATE_MIXER_CHANNEL_FRONT_RIGHT) - return OSS_STREAM_CONTROL (ctl)->priv->stereo == TRUE; + return OSS_STREAM_CONTROL (mmsc)->priv->stereo == TRUE; return FALSE; } static gboolean -oss_stream_control_set_balance (MateMixerStreamControl *ctl, gfloat balance) +oss_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance) { - OssStreamControl *octl; + OssStreamControl *control; + guint max; + gint v; - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); - octl = OSS_STREAM_CONTROL (ctl); + control = OSS_STREAM_CONTROL (mmsc); - if (octl->priv->stereo == FALSE) - return FALSE; + max = MAX (control->priv->volume[0], control->priv->volume[1]); + if (balance <= 0) { + control->priv->volume[1] = (balance + 1.0f) * max; + control->priv->volume[0] = max; + } else { + control->priv->volume[0] = (1.0f - balance) * max; + control->priv->volume[1] = max; + } - // XXX - return TRUE; + v = control->priv->volume[0] | (control->priv->volume[1] << 8); + + return write_volume (control, v); } static guint -oss_stream_control_get_min_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_min_volume (MateMixerStreamControl *mmsc) { return 0; } static guint -oss_stream_control_get_max_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_max_volume (MateMixerStreamControl *mmsc) { return 100; } static guint -oss_stream_control_get_normal_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_normal_volume (MateMixerStreamControl *mmsc) { return 100; } static guint -oss_stream_control_get_base_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_base_volume (MateMixerStreamControl *mmsc) { return 100; } + +static gboolean +write_volume (OssStreamControl *control, gint volume) +{ + gint ret; + + ret = ioctl (control->priv->fd, MIXER_WRITE (control->priv->devnum), &volume); + if (ret < 0) { + g_warning ("Failed to set volume: %s", g_strerror (errno)); + return FALSE; + } + return TRUE; +} diff --git a/backends/oss/oss-stream-control.h b/backends/oss/oss-stream-control.h index 420af48..c839faf 100644 --- a/backends/oss/oss-stream-control.h +++ b/backends/oss/oss-stream-control.h @@ -20,6 +20,7 @@ #include #include +#include G_BEGIN_DECLS @@ -42,7 +43,7 @@ typedef struct _OssStreamControlPrivate OssStreamControlPrivate; struct _OssStreamControl { - GObject parent; + MateMixerStreamControl parent; /*< private >*/ OssStreamControlPrivate *priv; @@ -50,24 +51,19 @@ struct _OssStreamControl struct _OssStreamControlClass { - GObjectClass parent; + MateMixerStreamControlClass parent; }; -GType oss_stream_control_get_type (void) G_GNUC_CONST; +GType oss_stream_control_get_type (void) G_GNUC_CONST; -OssStreamControl *oss_stream_control_new (gint fd, - gint dev_number, - const gchar *name, - const gchar *description, - gboolean stereo); +OssStreamControl *oss_stream_control_new (const gchar *name, + const gchar *label, + MateMixerStreamControlRole role, + gint fd, + gint devnum, + gboolean stereo); -gboolean oss_stream_control_update (OssStreamControl *octl); - -gboolean oss_stream_control_set_port (OssStreamControl *octl, - MateMixerPort *port); - -gboolean oss_stream_control_set_role (OssStreamControl *octl, - MateMixerStreamControlRole role); +gboolean oss_stream_control_update (OssStreamControl *control); G_END_DECLS diff --git a/backends/oss/oss-stream.c b/backends/oss/oss-stream.c index 69bfd06..5f0c629 100644 --- a/backends/oss/oss-stream.c +++ b/backends/oss/oss-stream.c @@ -15,301 +15,169 @@ * License along with this library; if not, see . */ -#include #include #include +#include -#include -#include -#include - +#include "oss-device.h" #include "oss-stream.h" #include "oss-stream-control.h" struct _OssStreamPrivate { - gchar *name; - gchar *description; - MateMixerDevice *device; - MateMixerStreamFlags flags; - MateMixerStreamState state; - GHashTable *ports; - GList *ports_list; - GHashTable *controls; - GList *controls_list; - OssStreamControl *control; -}; - -enum { - PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_DEVICE, - PROP_FLAGS, - PROP_STATE, - PROP_ACTIVE_PORT, + GHashTable *controls; + OssStreamControl *control; }; -static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); - static void oss_stream_class_init (OssStreamClass *klass); -static void oss_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); - static void oss_stream_init (OssStream *ostream); static void oss_stream_dispose (GObject *object); static void oss_stream_finalize (GObject *object); -G_DEFINE_TYPE_WITH_CODE (OssStream, oss_stream, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM, - mate_mixer_stream_interface_init)) - -static const gchar * oss_stream_get_name (MateMixerStream *stream); -static const gchar * oss_stream_get_description (MateMixerStream *stream); +G_DEFINE_TYPE (OssStream, oss_stream, MATE_MIXER_TYPE_STREAM) static MateMixerStreamControl *oss_stream_get_control (MateMixerStream *stream, const gchar *name); static MateMixerStreamControl *oss_stream_get_default_control (MateMixerStream *stream); -static const GList * oss_stream_list_controls (MateMixerStream *stream); -static const GList * oss_stream_list_ports (MateMixerStream *stream); - -static void -mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) -{ - iface->get_name = oss_stream_get_name; - iface->get_description = oss_stream_get_description; - iface->get_control = oss_stream_get_control; - iface->get_default_control = oss_stream_get_default_control; - iface->list_controls = oss_stream_list_controls; - iface->list_ports = oss_stream_list_ports; -} +static GList * oss_stream_list_controls (MateMixerStream *stream); static void oss_stream_class_init (OssStreamClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerStreamClass *stream_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = oss_stream_dispose; - object_class->finalize = oss_stream_finalize; - object_class->get_property = oss_stream_get_property; + object_class->dispose = oss_stream_dispose; + object_class->finalize = oss_stream_finalize; - g_object_class_override_property (object_class, PROP_NAME, "name"); - g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); - g_object_class_override_property (object_class, PROP_DEVICE, "device"); - g_object_class_override_property (object_class, PROP_FLAGS, "flags"); - g_object_class_override_property (object_class, PROP_STATE, "state"); - g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port"); + stream_class = MATE_MIXER_STREAM_CLASS (klass); + stream_class->get_control = oss_stream_get_control; + stream_class->get_default_control = oss_stream_get_default_control; + stream_class->list_controls = oss_stream_list_controls; g_type_class_add_private (object_class, sizeof (OssStreamPrivate)); } static void -oss_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) +oss_stream_init (OssStream *stream) { - OssStream *ostream; - - ostream = OSS_STREAM (object); - - switch (param_id) { - case PROP_NAME: - g_value_set_string (value, ostream->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, ostream->priv->description); - break; - case PROP_DEVICE: - g_value_set_object (value, ostream->priv->device); - break; - case PROP_FLAGS: - g_value_set_flags (value, ostream->priv->flags); - break; - case PROP_STATE: - /* Not supported */ - g_value_set_enum (value, MATE_MIXER_STREAM_STATE_UNKNOWN); - break; - case PROP_ACTIVE_PORT: - // XXX - g_value_set_object (value, NULL); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} + stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, + OSS_TYPE_STREAM, + OssStreamPrivate); -static void -oss_stream_init (OssStream *ostream) -{ - ostream->priv = G_TYPE_INSTANCE_GET_PRIVATE (ostream, - OSS_TYPE_STREAM, - OssStreamPrivate); - - ostream->priv->controls = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); - - ostream->priv->ports = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); + stream->priv->controls = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); } static void oss_stream_dispose (GObject *object) { - OssStream *ostream; + OssStream *stream; - ostream = OSS_STREAM (object); + stream = OSS_STREAM (object); - g_hash_table_remove_all (ostream->priv->controls); - g_hash_table_remove_all (ostream->priv->ports); + g_clear_object (&stream->priv->control); + g_hash_table_remove_all (stream->priv->controls); - G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object); + G_OBJECT_CLASS (oss_stream_parent_class)->dispose (object); } static void oss_stream_finalize (GObject *object) { - OssStream *ostream; - - ostream = OSS_STREAM (object); + OssStream *stream; - g_free (ostream->priv->name); - g_free (ostream->priv->description); + stream = OSS_STREAM (object); - g_hash_table_destroy (ostream->priv->controls); - g_hash_table_destroy (ostream->priv->ports); + g_hash_table_destroy (stream->priv->controls); G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object); } OssStream * oss_stream_new (const gchar *name, - const gchar *description, + MateMixerDevice *device, MateMixerStreamFlags flags) { OssStream *stream; - stream = g_object_new (OSS_TYPE_STREAM, NULL); - - stream->priv->name = g_strdup (name); - stream->priv->description = g_strdup (description); - stream->priv->flags = flags; - + stream = g_object_new (OSS_TYPE_STREAM, + "name", name, + "device", device, + "flags", flags, + NULL); return stream; } gboolean -oss_stream_add_control (OssStream *ostream, OssStreamControl *octl) +oss_stream_add_control (OssStream *stream, OssStreamControl *control) { const gchar *name; - g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); - name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (octl)); + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control)); - g_hash_table_insert (ostream->priv->controls, + g_hash_table_insert (stream->priv->controls, g_strdup (name), - octl); + control); return TRUE; } gboolean -oss_stream_set_default_control (OssStream *ostream, OssStreamControl *octl) +oss_stream_set_default_control (OssStream *stream, OssStreamControl *control) { - g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); + g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE); + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); /* This function is only used internally so avoid validating that the control * belongs to this stream */ - if (ostream->priv->control != NULL) - g_object_unref (ostream->priv->control); - - ostream->priv->control = g_object_ref (octl); - return TRUE; -} + if (stream->priv->control != NULL) + g_object_unref (stream->priv->control); -gboolean -oss_stream_add_port (OssStream *ostream, MateMixerPort *port) -{ - g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + if G_LIKELY (control != NULL) + stream->priv->control = g_object_ref (control); + else + stream->priv->control = NULL; - g_hash_table_insert (ostream->priv->ports, - g_strdup (mate_mixer_port_get_name (port)), - port); return TRUE; } -static const gchar * -oss_stream_get_name (MateMixerStream *stream) -{ - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - - return OSS_STREAM (stream)->priv->name; -} - -static const gchar * -oss_stream_get_description (MateMixerStream *stream) -{ - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - - return OSS_STREAM (stream)->priv->description; -} - static MateMixerStreamControl * -oss_stream_get_control (MateMixerStream *stream, const gchar *name) +oss_stream_get_control (MateMixerStream *mms, const gchar *name) { - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); + g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); g_return_val_if_fail (name != NULL, NULL); - return g_hash_table_lookup (OSS_STREAM (stream)->priv->controls, name); + return g_hash_table_lookup (OSS_STREAM (mms)->priv->controls, name); } static MateMixerStreamControl * -oss_stream_get_default_control (MateMixerStream *stream) -{ - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - - return MATE_MIXER_STREAM_CONTROL (OSS_STREAM (stream)->priv->control); -} - -static const GList * -oss_stream_list_controls (MateMixerStream *stream) +oss_stream_get_default_control (MateMixerStream *mms) { - OssStream *ostream; - - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - - ostream = OSS_STREAM (stream); + g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); - if (ostream->priv->controls_list == NULL) - ostream->priv->controls_list = g_hash_table_get_values (ostream->priv->controls); - - return ostream->priv->controls_list; + return MATE_MIXER_STREAM_CONTROL (OSS_STREAM (mms)->priv->control); } -static const GList * -oss_stream_list_ports (MateMixerStream *stream) +static GList * +oss_stream_list_controls (MateMixerStream *mms) { - OssStream *ostream; - - g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); + GList *list; - ostream = OSS_STREAM (stream); + g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); - if (ostream->priv->ports_list == NULL) - ostream->priv->ports_list = g_hash_table_get_values (ostream->priv->ports); + /* Convert the hash table to a linked list, this list is expected to be + * cached in the main library */ + list = g_hash_table_get_values (OSS_STREAM (mms)->priv->controls); + if (list != NULL) + g_list_foreach (list, (GFunc) g_object_ref, NULL); - return ostream->priv->ports_list; + return list; } diff --git a/backends/oss/oss-stream.h b/backends/oss/oss-stream.h index d6c2fb2..0470eb7 100644 --- a/backends/oss/oss-stream.h +++ b/backends/oss/oss-stream.h @@ -20,7 +20,9 @@ #include #include +#include +#include "oss-device.h" #include "oss-stream-control.h" G_BEGIN_DECLS @@ -44,7 +46,7 @@ typedef struct _OssStreamPrivate OssStreamPrivate; struct _OssStream { - GObject parent; + MateMixerStream parent; /*< private >*/ OssStreamPrivate *priv; @@ -52,23 +54,20 @@ struct _OssStream struct _OssStreamClass { - GObjectClass parent; + MateMixerStreamClass parent; }; -GType oss_stream_get_type (void) G_GNUC_CONST; +GType oss_stream_get_type (void) G_GNUC_CONST; -OssStream * oss_stream_new (const gchar *name, - const gchar *description, - MateMixerStreamFlags flags); +OssStream *oss_stream_new (const gchar *name, + MateMixerDevice *device, + MateMixerStreamFlags flags); -gboolean oss_stream_add_control (OssStream *stream, - OssStreamControl *ctl); +gboolean oss_stream_add_control (OssStream *stream, + OssStreamControl *control); -gboolean oss_stream_set_default_control (OssStream *stream, - OssStreamControl *ctl); - -gboolean oss_stream_add_port (OssStream *ostream, - MateMixerPort *port); +gboolean oss_stream_set_default_control (OssStream *stream, + OssStreamControl *control); G_END_DECLS 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 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#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: (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 - * - * 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 . - */ - -#ifndef OSS4_BACKEND_H -#define OSS4_BACKEND_H - -#include -#include - -#include - -#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-common.h b/backends/oss4/oss4-common.h deleted file mode 100644 index fe55b2b..0000000 --- a/backends/oss4/oss4-common.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#ifndef OSS4_COMMON_H -#define OSS4_COMMON_H - -#include "config.h" - -#include -#include -#include -#include - -#ifdef HAVE_SYS_SOUNDCARD_H -# include -#elif HAVE_SOUNDCARD_H -# include -#elif HAVE_MACHINE_SOUNDCARD_H -# include -#else -# error "No OSS4 header file present" -#endif - -#endif /* OSS4_COMMON_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 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "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 - * - * 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 . - */ - -#ifndef OSS4_DEVICE_H -#define OSS4_DEVICE_H - -#include -#include - -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 */ diff --git a/configure.ac b/configure.ac index b618913..d376b19 100644 --- a/configure.ac +++ b/configure.ac @@ -61,13 +61,12 @@ LT_INIT # ======================================================================= PKG_PROG_PKG_CONFIG -GLIB_REQUIRED_VERSION=2.36.0 +GLIB_REQUIRED_VERSION=2.32.0 PKG_CHECK_MODULES(GLIB, [ glib-2.0 >= $GLIB_REQUIRED_VERSION gobject-2.0 >= $GLIB_REQUIRED_VERSION gmodule-2.0 >= $GLIB_REQUIRED_VERSION - gio-2.0 >= $GLIB_REQUIRED_VERSION ]) GTK_DOC_CHECK([1.10], [--flavour no-tmpl]) @@ -119,6 +118,39 @@ AC_SUBST(HAVE_PULSEAUDIO) AC_SUBST(PULSEAUDIO_CFLAGS) AC_SUBST(PULSEAUDIO_LIBS) +# ----------------------------------------------------------------------- +# ALSA +# ----------------------------------------------------------------------- +ALSA_REQUIRED_VERSION=1.0.0 + +AC_ARG_ENABLE([alsa], + AS_HELP_STRING([--enable-alsa], + [Enable ALSA backend module @<:@default=auto@:>@]), + enable_alsa=$enableval, enable_alsa=auto) + +if test "x$enable_alsa" != "xno"; then + PKG_CHECK_MODULES(ALSA, [ + alsa >= $ALSA_REQUIRED_VERSION + gthread-2.0 >= $GLIB_REQUIRED_VERSION + ], + have_alsa=yes, + have_alsa=no) + + if test "x$enable_alsa" = "xyes" -a "x$have_alsa" = "xno"; then + AC_MSG_ERROR([ALSA support explicitly requested but dependencies not found]) + fi + + if test "x$have_alsa" = "xyes" ; then + AC_DEFINE(HAVE_ALSA, [], [Define if we have ALSA support]) + fi +fi + +AM_CONDITIONAL(HAVE_ALSA, test "x$have_alsa" = "xyes") + +AC_SUBST(HAVE_ALSA) +AC_SUBST(ALSA_CFLAGS) +AC_SUBST(ALSA_LIBS) + # ----------------------------------------------------------------------- # OSS # ----------------------------------------------------------------------- @@ -155,40 +187,6 @@ AC_SUBST(HAVE_OSS) AC_SUBST(OSS_CFLAGS) AC_SUBST(OSS_LIBS) -# ----------------------------------------------------------------------- -# OSS4 -# ----------------------------------------------------------------------- -AC_ARG_ENABLE([oss4], - AS_HELP_STRING([--enable-oss4], - [Enable OSS4 backend module @<:@default=no@:>@]), - enable_oss4=$enableval, enable_oss4=no) - -if test "x$enable_oss4" != "xno"; then - AC_CHECK_HEADERS([soundcard.h sys/soundcard.h machine/soundcard.h]) - if test "x$ac_cv_header_soundcard_h" = "xyes" -o \ - "x$ac_cv_header_sys_soundcard_h" = "xyes" -o \ - "x$ac_cv_header_machine_soundcard_h" = "xyes"; then - have_oss4=yes - else - have_oss4=no - fi - - if test "x$enable_oss4" = "xyes" -a "x$have_oss4" = "xno"; then - AC_MSG_ERROR([OSS4 support explicitly requested but dependencies not found]) - fi - - if test "x$have_oss4" = "xyes" ; then - AC_DEFINE(HAVE_OSS4, [], [Define if we have OSS support]) - fi -else - have_oss4=no -fi - -AM_CONDITIONAL(HAVE_OSS4, test "x$have_oss4" = "xyes") - -AC_SUBST(HAVE_OSS4) -AC_SUBST(OSS4_CFLAGS) - # ======================================================================= # Compiler warnings # ======================================================================= @@ -240,9 +238,9 @@ Makefile libmatemixer/Makefile backends/Makefile backends/null/Makefile -backends/oss/Makefile -backends/oss4/Makefile backends/pulse/Makefile +backends/alsa/Makefile +backends/oss/Makefile data/Makefile data/libmatemixer.pc docs/Makefile @@ -266,7 +264,7 @@ echo " Build Null module: $have_null Build PulseAudio module: $have_pulseaudio + Build ALSA module: $have_alsa Build OSS module: $have_oss - Build OSS4 module: $have_oss4 " diff --git a/examples/monitor.c b/examples/monitor.c index dca5105..71d8b61 100644 --- a/examples/monitor.c +++ b/examples/monitor.c @@ -26,7 +26,7 @@ #include -static MateMixerControl *control; +static MateMixerContext *context; static GMainLoop *mainloop; static gchar * @@ -56,6 +56,30 @@ create_app_string (const gchar *app_name, return g_string_free (string, FALSE); } +static const gchar * +get_stream_control_role_string (MateMixerStreamControlRole role) +{ + switch (role) { + case MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN: + return "Unknown"; + case MATE_MIXER_STREAM_CONTROL_ROLE_MASTER: + return "Master"; + case MATE_MIXER_STREAM_CONTROL_ROLE_PCM: + return "PCM"; + case MATE_MIXER_STREAM_CONTROL_ROLE_BASS: + return "Bass"; + case MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE: + return "Treble"; + case MATE_MIXER_STREAM_CONTROL_ROLE_CD: + return "CD"; + case MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER: + return "PC Speaker"; + case MATE_MIXER_STREAM_CONTROL_ROLE_PORT: + return "Port"; + } + return "Unknown"; +} + static const gchar * create_role_string (MateMixerClientStreamRole role) { @@ -122,39 +146,21 @@ static void print_devices (void) { const GList *devices; - const GList *ports; const GList *profiles; MateMixerDeviceProfile *active_profile; - devices = mate_mixer_control_list_devices (control); + devices = mate_mixer_context_list_devices (context); while (devices) { MateMixerDevice *device = MATE_MIXER_DEVICE (devices->data); g_print ("Device %s\n" - " |-| Description : %s\n" - " |-| Icon Name : %s\n\n", + " |-| Label : %s\n" + " |-| Icon Name : %s\n\n", mate_mixer_device_get_name (device), - mate_mixer_device_get_description (device), + mate_mixer_device_get_label (device), mate_mixer_device_get_icon (device)); - ports = mate_mixer_device_list_ports (device); - while (ports) { - MateMixerPort *port = MATE_MIXER_PORT (ports->data); - - g_print (" |-| Port %s\n" - " |-| Description : %s\n" - " |-| Icon Name : %s\n" - " |-| Priority : %u\n" - " |-| Status : \n\n", - mate_mixer_port_get_name (port), - mate_mixer_port_get_description (port), - mate_mixer_port_get_icon (port), - mate_mixer_port_get_priority (port)); - - ports = ports->next; - } - profiles = mate_mixer_device_list_profiles (device); active_profile = mate_mixer_device_get_active_profile (device); @@ -162,15 +168,15 @@ print_devices (void) MateMixerDeviceProfile *profile = MATE_MIXER_DEVICE_PROFILE (profiles->data); g_print (" |%c| Profile %s\n" - " |-| Description : %s\n" - " |-| Priority : %u\n" - " |-| Inputs : %u\n" - " |-| Outputs : %u\n\n", + " |-| Label : %s\n" + " |-| Priority : %u\n" + " |-| Inputs : %u\n" + " |-| Outputs : %u\n\n", (profile == active_profile) ? 'A' : '-', mate_mixer_device_profile_get_name (profile), - mate_mixer_device_profile_get_description (profile), + mate_mixer_device_profile_get_label (profile), mate_mixer_device_profile_get_priority (profile), mate_mixer_device_profile_get_num_input_streams (profile), mate_mixer_device_profile_get_num_output_streams (profile)); @@ -179,6 +185,35 @@ print_devices (void) } g_print ("\n"); + const GList *switches = mate_mixer_device_list_switches (device); + + while (switches != NULL) { + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (switches->data); + MateMixerSwitchOption *active = mate_mixer_switch_get_active_option (swtch); + + const GList *options; + + options = mate_mixer_switch_list_options (swtch); + + g_print ("Switch %s\n", + mate_mixer_switch_get_name (swtch)); + + while (options != NULL) { + MateMixerSwitchOption *option = MATE_MIXER_SWITCH_OPTION (options->data); + + g_print (" |%c| %s\n", + (option == active) + ? '*' + : '-', + mate_mixer_switch_option_get_label (option)); + + options = options->next; + } + + switches = switches->next; + } + g_print ("\n"); + devices = devices->next; } } @@ -187,42 +222,85 @@ static void print_streams (void) { const GList *streams; - const GList *ports; - streams = mate_mixer_control_list_streams (control); + streams = mate_mixer_context_list_streams (context); while (streams) { MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); MateMixerStreamControl *ctl; + MateMixerSwitch *swtch; MateMixerClientStream *client = NULL; gchar *volume_bar; gdouble volume; + const GList *controls; + const GList *switches; + if (mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CLIENT) { /* The application-specific details are accessible through the client * interface, which all client streams implement */ client = MATE_MIXER_CLIENT_STREAM (stream); } - ctl = mate_mixer_stream_get_default_control (stream); + controls = mate_mixer_stream_list_controls (stream); - volume_bar = create_volume_bar (ctl, &volume); + while (controls != NULL) { + ctl = MATE_MIXER_STREAM_CONTROL (controls->data); - g_print ("Stream %s\n" - " |-| Description : %s\n" - " |-| Volume : %s %.1f %%\n" - " |-| Muted : %s\n" - " |-| Channels : %d\n" - " |-| Balance : %.1f\n" - " |-| Fade : %.1f\n", - mate_mixer_stream_get_name (stream), - mate_mixer_stream_get_description (stream), - volume_bar, - volume, - mate_mixer_stream_control_get_mute (ctl) ? "Yes" : "No", - mate_mixer_stream_control_get_num_channels (ctl), - mate_mixer_stream_control_get_balance (ctl), - mate_mixer_stream_control_get_fade (ctl)); + const gchar *role; + + role = get_stream_control_role_string (mate_mixer_stream_control_get_role (ctl)); + + // XXX volume is sometimes -nan, use flags + volume_bar = create_volume_bar (ctl, &volume); + + g_print ("Stream %s control %s / %s\n" + " |-| Volume : %s %.1f %% (%.1f dB)\n" + " |-| Muted : %s\n" + " |-| Channels : %d\n" + " |-| Balance : %.1f\n" + " |-| Fade : %.1f\n" + " |-| Role : %s\n", + mate_mixer_stream_get_name (stream), + mate_mixer_stream_control_get_name (ctl), + mate_mixer_stream_control_get_label (ctl), + volume_bar, + volume, + mate_mixer_stream_control_get_decibel (ctl), + mate_mixer_stream_control_get_mute (ctl) ? "Yes" : "No", + mate_mixer_stream_control_get_num_channels (ctl), + mate_mixer_stream_control_get_balance (ctl), + mate_mixer_stream_control_get_fade (ctl), + role); + + g_free (volume_bar); + + controls = controls->next; + } + + switches = mate_mixer_stream_list_switches (stream); + + while (switches != NULL) { + swtch = MATE_MIXER_SWITCH (switches->data); + const GList *options; + + options = mate_mixer_switch_list_options (swtch); + + g_print ("Switch %s\n", + mate_mixer_switch_get_name (swtch)); + + while (options != NULL) { + MateMixerSwitchOption *option = MATE_MIXER_SWITCH_OPTION (options->data); + + g_print (" |%c| %s\n", + '-', + mate_mixer_switch_option_get_label (option)); + + options = options->next; + } + + options = options->next; + } if (client != NULL) { MateMixerClientStreamFlags client_flags; @@ -240,35 +318,17 @@ print_streams (void) } g_print ("\n"); - g_free (volume_bar); - - ports = mate_mixer_stream_list_ports (stream); - while (ports) { - MateMixerPort *port = MATE_MIXER_PORT (ports->data); - - g_print (" |-| Port %s\n" - " |-| Description : %s\n" - " |-| Icon Name : %s\n" - " |-| Priority : %u\n" - " |-| Status : \n\n", - mate_mixer_port_get_name (port), - mate_mixer_port_get_description (port), - mate_mixer_port_get_icon (port), - mate_mixer_port_get_priority (port)); - - ports = ports->next; - } streams = streams->next; } } static void -print_cached_streams (void) +print_stored_streams (void) { const GList *streams; - streams = mate_mixer_control_list_cached_streams (control); + streams = mate_mixer_context_list_stored_streams (context); while (streams) { MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); @@ -287,7 +347,7 @@ print_cached_streams (void) volume_bar = create_volume_bar (ctl, &volume); - g_print ("Cached stream %s\n" + g_print ("Stored stream %s\n" " |-| Role : %s\n" " |-| Volume : %s %.1f %%\n" " |-| Muted : %s\n" @@ -323,11 +383,11 @@ static void connected (void) { g_print ("Connected using the %s backend.\n\n", - mate_mixer_control_get_backend_name (control)); + mate_mixer_context_get_backend_name (context)); print_devices (); print_streams (); - print_cached_streams (); + print_stored_streams (); g_print ("Waiting for events. Hit CTRL+C to quit.\n"); } @@ -337,7 +397,7 @@ state_cb (void) { MateMixerState state; - state = mate_mixer_control_get_state (control); + state = mate_mixer_context_get_state (context); switch (state) { case MATE_MIXER_STATE_READY: @@ -353,25 +413,25 @@ state_cb (void) } static void -device_added_cb (MateMixerControl *control, const gchar *name) +device_added_cb (MateMixerContext *context, const gchar *name) { g_print ("Device added: %s\n", name); } static void -device_removed_cb (MateMixerControl *control, const gchar *name) +device_removed_cb (MateMixerContext *context, const gchar *name) { g_print ("Device removed: %s\n", name); } static void -stream_added_cb (MateMixerControl *control, const gchar *name) +stream_added_cb (MateMixerContext *context, const gchar *name) { g_print ("Stream added: %s\n", name); } static void -stream_removed_cb (MateMixerControl *control, const gchar *name) +stream_removed_cb (MateMixerContext *context, const gchar *name) { g_print ("Stream removed: %s\n", name); } @@ -388,22 +448,22 @@ signal_cb (gpointer mainloop) int main (int argc, char *argv[]) { MateMixerState state; - GOptionContext *context; + GOptionContext *ctx; gchar *backend = NULL; gchar *server = NULL; GError *error = NULL; GOptionEntry entries[] = { - { "backend", 'b', 0, G_OPTION_ARG_STRING, &backend, "Sound system to use (pulseaudio, oss, oss4, null)", NULL }, + { "backend", 'b', 0, G_OPTION_ARG_STRING, &backend, "Sound system to use (pulseaudio, alsa, oss, null)", NULL }, { "server", 's', 0, G_OPTION_ARG_STRING, &server, "Sound server address", NULL }, { NULL } }; - context = g_option_context_new ("- libmatemixer monitor"); + ctx = g_option_context_new ("- libmatemixer monitor"); - g_option_context_add_main_entries (context, entries, NULL); + g_option_context_add_main_entries (ctx, entries, NULL); - if (!g_option_context_parse (context, &argc, &argv, &error)) { + if (!g_option_context_parse (ctx, &argc, &argv, &error)) { g_printerr ("%s\n", error->message); g_error_free (error); return 1; @@ -415,24 +475,24 @@ int main (int argc, char *argv[]) setlocale (LC_ALL, ""); - /* Set up the controller, through which we access the main functionality */ - control = mate_mixer_control_new (); + /* Set up the contextler, through which we access the main functionality */ + context = mate_mixer_context_new (); /* Some details about our application, only used with the PulseAudio backend */ - mate_mixer_control_set_app_name (control, "MateMixer Monitor"); - mate_mixer_control_set_app_id (control, "org.mate-desktop.libmatemixer-monitor"); - mate_mixer_control_set_app_version (control, "1.0"); - mate_mixer_control_set_app_icon (control, "multimedia-volume-control"); + mate_mixer_context_set_app_name (context, "MateMixer Monitor"); + mate_mixer_context_set_app_id (context, "org.mate-desktop.libmatemixer-monitor"); + mate_mixer_context_set_app_version (context, "1.0"); + mate_mixer_context_set_app_icon (context, "multimedia-volume-context"); if (backend) { if (!strcmp (backend, "pulseaudio")) - mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_PULSEAUDIO); + mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_PULSEAUDIO); + else if (!strcmp (backend, "alsa")) + mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_ALSA); else if (!strcmp (backend, "oss")) - mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_OSS); - else if (!strcmp (backend, "oss4")) - mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_OSS4); + mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_OSS); else if (!strcmp (backend, "null")) - mate_mixer_control_set_backend_type (control, MATE_MIXER_BACKEND_NULL); + mate_mixer_context_set_backend_type (context, MATE_MIXER_BACKEND_NULL); else g_printerr ("Sound system backend '%s' is unknown, it will be auto-detected.\n", backend); @@ -441,40 +501,39 @@ int main (int argc, char *argv[]) } if (server) { - mate_mixer_control_set_server_address (control, server); + mate_mixer_context_set_server_address (context, server); g_free (server); } /* Initiate connection to the sound server */ - if (!mate_mixer_control_open (control)) { - g_object_unref (control); - mate_mixer_deinit (); + if (!mate_mixer_context_open (context)) { + g_object_unref (context); return 1; } - g_signal_connect (G_OBJECT (control), + g_signal_connect (G_OBJECT (context), "device-added", G_CALLBACK (device_added_cb), NULL); - g_signal_connect (G_OBJECT (control), + g_signal_connect (G_OBJECT (context), "device-removed", G_CALLBACK (device_removed_cb), NULL); - g_signal_connect (G_OBJECT (control), + g_signal_connect (G_OBJECT (context), "stream-added", G_CALLBACK (stream_added_cb), NULL); - g_signal_connect (G_OBJECT (control), + g_signal_connect (G_OBJECT (context), "stream-removed", G_CALLBACK (stream_removed_cb), NULL); - /* If mate_mixer_control_open() returns TRUE, the state will be either + /* If mate_mixer_context_open() returns TRUE, the state will be either * MATE_MIXER_STATE_READY or MATE_MIXER_STATE_CONNECTING. * - * In case mate_mixer_control_open() returned FALSE, the current state + * In case mate_mixer_context_open() returned FALSE, the current state * would be MATE_MIXER_STATE_FAILED */ - state = mate_mixer_control_get_state (control); + state = mate_mixer_context_get_state (context); switch (state) { case MATE_MIXER_STATE_READY: @@ -484,7 +543,7 @@ int main (int argc, char *argv[]) g_print ("Waiting for connection...\n"); /* Wait for the state transition */ - g_signal_connect (control, + g_signal_connect (context, "notify::state", G_CALLBACK (state_cb), NULL); @@ -503,8 +562,7 @@ int main (int argc, char *argv[]) g_main_loop_run (mainloop); - g_object_unref (control); - mate_mixer_deinit (); + g_object_unref (context); return 0; } diff --git a/libmatemixer/Makefile.am b/libmatemixer/Makefile.am index 9666684..8858b90 100644 --- a/libmatemixer/Makefile.am +++ b/libmatemixer/Makefile.am @@ -11,13 +11,16 @@ libmatemixer_includedir = $(includedir)/libmatemixer libmatemixer_include_HEADERS = \ matemixer.h \ matemixer-client-stream.h \ - matemixer-control.h \ + matemixer-context.h \ matemixer-device.h \ matemixer-device-profile.h \ matemixer-enums.h \ - matemixer-port.h \ matemixer-stream.h \ matemixer-stream-control.h \ + matemixer-switch.h \ + matemixer-switch-option.h \ + matemixer-toggle.h \ + matemixer-types.h \ matemixer-version.h libmatemixer_la_CFLAGS = $(GLIB_CFLAGS) @@ -27,19 +30,24 @@ libmatemixer_la_SOURCES = \ matemixer-private.h \ matemixer-backend.c \ matemixer-backend.h \ + matemixer-backend-private.h \ matemixer-backend-module.c \ matemixer-backend-module.h \ matemixer-client-stream.c \ - matemixer-control.c \ + matemixer-context.c \ matemixer-device.c \ matemixer-device-profile.c \ matemixer-device-profile-private.h \ matemixer-enum-types.c \ matemixer-enum-types.h \ - matemixer-port.c \ - matemixer-port-private.h \ matemixer-stream.c \ - matemixer-stream-control.c + matemixer-stream-control.c \ + matemixer-stream-control-private.h \ + matemixer-switch.c \ + matemixer-switch-private.h \ + matemixer-switch-option.c \ + matemixer-switch-option-private.h \ + matemixer-toggle.c libmatemixer_la_LIBADD = $(GLIB_LIBS) diff --git a/libmatemixer/matemixer-backend-module.c b/libmatemixer/matemixer-backend-module.c index a3146d2..0e7716e 100644 --- a/libmatemixer/matemixer-backend-module.c +++ b/libmatemixer/matemixer-backend-module.c @@ -22,16 +22,19 @@ #include "matemixer-backend.h" #include "matemixer-backend-module.h" -struct _MateMixerBackendModulePrivate -{ - GModule *gmodule; - gchar *path; - gboolean loaded; +/* Initialize backend */ +typedef void (*BackendInit) (GTypeModule *type_module); - void (*init) (GTypeModule *type_module); - void (*deinit) (void); +/* Return information about backend */ +typedef const MateMixerBackendInfo *(*BackendGetInfo) (void); - const MateMixerBackendInfo *(*get_info) (void); +struct _MateMixerBackendModulePrivate +{ + GModule *gmodule; + gchar *path; + gboolean loaded; + BackendInit init; + BackendGetInfo get_info; }; enum { @@ -102,6 +105,7 @@ mate_mixer_backend_module_get_property (GObject *object, case PROP_PATH: g_value_set_string (value, module->priv->path); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -122,7 +126,10 @@ mate_mixer_backend_module_set_property (GObject *object, case PROP_PATH: /* Construct-only string */ module->priv->path = g_strdup (g_value_get_string (value)); + + g_type_module_set_name (G_TYPE_MODULE (object), module->priv->path); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -157,8 +164,11 @@ mate_mixer_backend_module_dispose (GObject *object) static void mate_mixer_backend_module_finalize (GObject *object) { - /* This is in fact never called */ - g_free (MATE_MIXER_BACKEND_MODULE (object)->priv->path); + MateMixerBackendModule *module; + + module = MATE_MIXER_BACKEND_MODULE (object); + + g_free (module->priv->path); G_OBJECT_CLASS (mate_mixer_backend_module_parent_class)->finalize (object); } @@ -174,17 +184,11 @@ mate_mixer_backend_module_finalize (GObject *object) MateMixerBackendModule * mate_mixer_backend_module_new (const gchar *path) { - MateMixerBackendModule *module; - g_return_val_if_fail (path != NULL, NULL); - module = g_object_new (MATE_MIXER_TYPE_BACKEND_MODULE, - "path", path, - NULL); - - g_type_module_set_name (G_TYPE_MODULE (module), path); - - return module; + return g_object_new (MATE_MIXER_TYPE_BACKEND_MODULE, + "path", path, + NULL); } /** @@ -227,76 +231,57 @@ backend_module_load (GTypeModule *type_module) module = MATE_MIXER_BACKEND_MODULE (type_module); - if (module->priv->loaded == FALSE) { - module->priv->gmodule = g_module_open (module->priv->path, - G_MODULE_BIND_LAZY | - G_MODULE_BIND_LOCAL); - if (module->priv->gmodule == NULL) { - g_warning ("Failed to load backend module %s: %s", - module->priv->path, - g_module_error ()); - - return FALSE; - } - - /* Validate library symbols that each backend module must provide */ - if (g_module_symbol (module->priv->gmodule, - "backend_module_init", - (gpointer *) &module->priv->init) == FALSE || - g_module_symbol (module->priv->gmodule, - "backend_module_get_info", - (gpointer *) &module->priv->get_info) == FALSE) { - g_warning ("Failed to load backend module %s: %s", - module->priv->path, - g_module_error ()); - - g_module_close (module->priv->gmodule); - return FALSE; - } - - /* Optional backend function */ + if (module->priv->loaded == TRUE) + return TRUE; + + module->priv->gmodule = g_module_open (module->priv->path, + G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (module->priv->gmodule == NULL) { + g_warning ("Failed to load backend module %s: %s", + module->priv->path, + g_module_error ()); + + return FALSE; + } + + /* Validate library symbols that each backend module must provide */ + if (g_module_symbol (module->priv->gmodule, + "backend_module_init", + (gpointer *) &module->priv->init) == FALSE || g_module_symbol (module->priv->gmodule, - "backend_module_deinit", - (gpointer *) &module->priv->deinit); - - module->priv->init (type_module); - module->priv->loaded = TRUE; - - /* Make sure get_info() returns something, so we can avoid checking it - * in other parts of the library */ - if (G_UNLIKELY (module->priv->get_info () == NULL)) { - g_critical ("Backend module %s does not provide module information", - module->priv->path); - - /* Close the module but keep the loaded flag to avoid unreffing - * this instance as the GType has most likely been registered */ - g_module_close (module->priv->gmodule); - return FALSE; - } - - /* It is not possible to unref this instance, so let's avoid unloading - * the module and just let the backend module (de)initialize when - * (de)init functions are called repeatedly */ - g_module_make_resident (module->priv->gmodule); - - g_debug ("Loaded backend module %s", module->priv->path); - } else { - /* This function was called before, so avoid loading and initialize only */ - module->priv->init (type_module); + "backend_module_get_info", + (gpointer *) &module->priv->get_info) == FALSE) { + g_warning ("Failed to load backend module %s: %s", + module->priv->path, + g_module_error ()); + + g_module_close (module->priv->gmodule); + return FALSE; + } + + module->priv->init (type_module); + module->priv->loaded = TRUE; + + /* Make sure get_info() returns something, so we can avoid checking it + * in other parts of the library */ + if (G_UNLIKELY (module->priv->get_info () == NULL)) { + g_critical ("Backend module %s does not provide module information", + module->priv->path); + + /* Close the module but keep the loaded flag to avoid unreffing + * this instance as the GType has most likely been registered */ + g_module_close (module->priv->gmodule); + return FALSE; } + /* It is not possible to unref this instance, so keep the module alive */ + g_module_make_resident (module->priv->gmodule); + + g_debug ("Loaded backend module %s", module->priv->path); return TRUE; } static void backend_module_unload (GTypeModule *type_module) { - MateMixerBackendModule *module; - - module = MATE_MIXER_BACKEND_MODULE (type_module); - - /* Only deinitialize the backend module, do not modify the loaded flag - * as the module remains loaded */ - if (module->priv->deinit) - module->priv->deinit (); } diff --git a/libmatemixer/matemixer-backend-module.h b/libmatemixer/matemixer-backend-module.h index 62b0a43..e1dfd8d 100644 --- a/libmatemixer/matemixer-backend-module.h +++ b/libmatemixer/matemixer-backend-module.h @@ -21,17 +21,10 @@ #include #include -#include "matemixer-enums.h" +#include G_BEGIN_DECLS -typedef struct { - gchar *name; - guint priority; - GType g_type; - MateMixerBackendType backend_type; -} MateMixerBackendInfo; - #define MATE_MIXER_TYPE_BACKEND_MODULE \ (mate_mixer_backend_module_get_type ()) #define MATE_MIXER_BACKEND_MODULE(o) \ @@ -45,6 +38,7 @@ typedef struct { #define MATE_MIXER_BACKEND_MODULE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_BACKEND_MODULE, MateMixerBackendModuleClass)) +typedef struct _MateMixerBackendInfo MateMixerBackendInfo; typedef struct _MateMixerBackendModule MateMixerBackendModule; typedef struct _MateMixerBackendModuleClass MateMixerBackendModuleClass; typedef struct _MateMixerBackendModulePrivate MateMixerBackendModulePrivate; @@ -62,6 +56,14 @@ struct _MateMixerBackendModuleClass GTypeModuleClass parent_class; }; +struct _MateMixerBackendInfo +{ + gchar *name; + guint priority; + GType g_type; + MateMixerBackendType backend_type; +}; + GType mate_mixer_backend_module_get_type (void) G_GNUC_CONST; MateMixerBackendModule * mate_mixer_backend_module_new (const gchar *path); diff --git a/libmatemixer/matemixer-backend-private.h b/libmatemixer/matemixer-backend-private.h new file mode 100644 index 0000000..b5de8ae --- /dev/null +++ b/libmatemixer/matemixer-backend-private.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_BACKEND_PRIVATE_H +#define MATEMIXER_BACKEND_PRIVATE_H + +#include + +#include "matemixer-backend.h" +#include "matemixer-enums.h" +#include "matemixer-stream.h" + +G_BEGIN_DECLS + +void _mate_mixer_backend_set_state (MateMixerBackend *backend, + MateMixerState state); + +void _mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream); + +void _mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream); + +G_END_DECLS + +#endif /* MATEMIXER_BACKEND_PRIVATE_H */ diff --git a/libmatemixer/matemixer-backend.c b/libmatemixer/matemixer-backend.c index e070090..fab0883 100644 --- a/libmatemixer/matemixer-backend.c +++ b/libmatemixer/matemixer-backend.c @@ -15,61 +15,122 @@ * License along with this library; if not, see . */ +#include #include #include #include "matemixer-backend.h" +#include "matemixer-backend-private.h" #include "matemixer-enums.h" #include "matemixer-enum-types.h" #include "matemixer-stream.h" +struct _MateMixerBackendPrivate +{ + GList *devices; + GList *streams; + GList *stored_streams; + MateMixerStream *default_input; + MateMixerStream *default_output; + MateMixerState state; + MateMixerBackendFlags flags; +}; + +enum { + PROP_0, + PROP_STATE, + PROP_DEFAULT_INPUT_STREAM, + PROP_DEFAULT_OUTPUT_STREAM, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + enum { DEVICE_ADDED, DEVICE_REMOVED, STREAM_ADDED, STREAM_REMOVED, - CACHED_STREAM_ADDED, - CACHED_STREAM_REMOVED, + STORED_STREAM_ADDED, + STORED_STREAM_REMOVED, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0, }; -G_DEFINE_INTERFACE (MateMixerBackend, mate_mixer_backend, G_TYPE_OBJECT) +static void mate_mixer_backend_class_init (MateMixerBackendClass *klass); + +static void mate_mixer_backend_init (MateMixerBackend *backend); + +static void mate_mixer_backend_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_backend_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_backend_dispose (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (MateMixerBackend, mate_mixer_backend, G_TYPE_OBJECT) + +static void device_added (MateMixerBackend *backend, const gchar *name); +static void device_removed (MateMixerBackend *backend, const gchar *name); + +static void device_stream_added (MateMixerDevice *device, + const gchar *name); +static void device_stream_removed (MateMixerDevice *device, + const gchar *name); + +static void free_devices (MateMixerBackend *backend); +static void free_streams (MateMixerBackend *backend); +static void free_stored_streams (MateMixerBackend *backend); +static void stream_removed (MateMixerBackend *backend, + const gchar *name); static void -mate_mixer_backend_default_init (MateMixerBackendInterface *iface) +mate_mixer_backend_class_init (MateMixerBackendClass *klass) { - g_object_interface_install_property (iface, - g_param_spec_enum ("state", - "State", - "Current backend connection state", - MATE_MIXER_TYPE_STATE, - MATE_MIXER_STATE_IDLE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_object ("default-input", - "Default input", - "Default input stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_object ("default-output", - "Default output", - "Default output stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = mate_mixer_backend_dispose; + object_class->get_property = mate_mixer_backend_get_property; + object_class->set_property = mate_mixer_backend_set_property; + + properties[PROP_STATE] = + g_param_spec_enum ("state", + "State", + "Current backend connection state", + MATE_MIXER_TYPE_STATE, + MATE_MIXER_STATE_IDLE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_DEFAULT_INPUT_STREAM] = + g_param_spec_object ("default-input-stream", + "Default input stream", + "Default input stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_DEFAULT_OUTPUT_STREAM] = + g_param_spec_object ("default-output-stream", + "Default output stream", + "Default output stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); signals[DEVICE_ADDED] = g_signal_new ("device-added", - G_TYPE_FROM_INTERFACE (iface), + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, device_added), + G_STRUCT_OFFSET (MateMixerBackendClass, device_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -79,9 +140,9 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) signals[DEVICE_REMOVED] = g_signal_new ("device-removed", - G_TYPE_FROM_INTERFACE (iface), + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, device_removed), + G_STRUCT_OFFSET (MateMixerBackendClass, device_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -91,9 +152,9 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) signals[STREAM_ADDED] = g_signal_new ("stream-added", - G_TYPE_FROM_INTERFACE (iface), + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, stream_added), + G_STRUCT_OFFSET (MateMixerBackendClass, stream_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -103,9 +164,9 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) signals[STREAM_REMOVED] = g_signal_new ("stream-removed", - G_TYPE_FROM_INTERFACE (iface), + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, stream_removed), + G_STRUCT_OFFSET (MateMixerBackendClass, stream_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -113,11 +174,11 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) 1, G_TYPE_STRING); - signals[CACHED_STREAM_ADDED] = - g_signal_new ("cached-stream-added", - G_TYPE_FROM_INTERFACE (iface), + signals[STORED_STREAM_ADDED] = + g_signal_new ("stored-stream-added", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, cached_stream_added), + G_STRUCT_OFFSET (MateMixerBackendClass, stored_stream_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -125,30 +186,149 @@ mate_mixer_backend_default_init (MateMixerBackendInterface *iface) 1, G_TYPE_STRING); - signals[CACHED_STREAM_REMOVED] = - g_signal_new ("cached-stream-removed", - G_TYPE_FROM_INTERFACE (iface), + signals[STORED_STREAM_REMOVED] = + g_signal_new ("stored-stream-removed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendInterface, cached_stream_removed), + G_STRUCT_OFFSET (MateMixerBackendClass, stored_stream_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + + g_type_class_add_private (object_class, sizeof (MateMixerBackendPrivate)); +} + +static void +mate_mixer_backend_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerBackend *backend; + + backend = MATE_MIXER_BACKEND (object); + + switch (param_id) { + case PROP_STATE: + g_value_set_enum (value, backend->priv->state); + break; + + case PROP_DEFAULT_INPUT_STREAM: + g_value_set_object (value, backend->priv->default_input); + break; + + case PROP_DEFAULT_OUTPUT_STREAM: + g_value_set_object (value, backend->priv->default_output); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_backend_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerBackend *backend; + + backend = MATE_MIXER_BACKEND (object); + + switch (param_id) { + case PROP_DEFAULT_INPUT_STREAM: + mate_mixer_backend_set_default_input_stream (backend, g_value_get_object (value)); + break; + + case PROP_DEFAULT_OUTPUT_STREAM: + mate_mixer_backend_set_default_output_stream (backend, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_backend_init (MateMixerBackend *backend) +{ + backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, + MATE_MIXER_TYPE_BACKEND, + MateMixerBackendPrivate); + + g_signal_connect (G_OBJECT (backend), + "device-added", + G_CALLBACK (free_devices), + NULL); + g_signal_connect (G_OBJECT (backend), + "device-added", + G_CALLBACK (device_added), + NULL); + + g_signal_connect (G_OBJECT (backend), + "device-removed", + G_CALLBACK (free_devices), + NULL); + g_signal_connect (G_OBJECT (backend), + "device-removed", + G_CALLBACK (device_removed), + NULL); + + g_signal_connect (G_OBJECT (backend), + "stream-added", + G_CALLBACK (free_streams), + NULL); + g_signal_connect (G_OBJECT (backend), + "stream-removed", + G_CALLBACK (free_streams), + NULL); + + g_signal_connect (G_OBJECT (backend), + "stored-stream-added", + G_CALLBACK (free_stored_streams), + NULL); + g_signal_connect (G_OBJECT (backend), + "stored-stream-removed", + G_CALLBACK (free_stored_streams), + NULL); + + // XXX also free when changing state +} + +static void +mate_mixer_backend_dispose (GObject *object) +{ + MateMixerBackend *backend; + + backend = MATE_MIXER_BACKEND (object); + + free_devices (backend); + free_streams (backend); + free_stored_streams (backend); + + g_clear_object (&backend->priv->default_input); + g_clear_object (&backend->priv->default_output); + + G_OBJECT_CLASS (mate_mixer_backend_parent_class)->dispose (object); } void -mate_mixer_backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data) +mate_mixer_backend_set_data (MateMixerBackend *backend, MateMixerBackendData *data) { - MateMixerBackendInterface *iface; + MateMixerBackendClass *klass; g_return_if_fail (MATE_MIXER_IS_BACKEND (backend)); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); - if (iface->set_data) - iface->set_data (backend, data); + if (klass->set_data != NULL) + klass->set_data (backend, data); } gboolean @@ -157,79 +337,87 @@ mate_mixer_backend_open (MateMixerBackend *backend) g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), FALSE); /* Implementation required */ - return MATE_MIXER_BACKEND_GET_INTERFACE (backend)->open (backend); + return MATE_MIXER_BACKEND_GET_CLASS (backend)->open (backend); } void mate_mixer_backend_close (MateMixerBackend *backend) { - MateMixerBackendInterface *iface; + MateMixerBackendClass *klass; g_return_if_fail (MATE_MIXER_IS_BACKEND (backend)); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); - if (iface->close) - iface->close (backend); + if (klass->close != NULL) + klass->close (backend); } MateMixerState mate_mixer_backend_get_state (MateMixerBackend *backend) { - MateMixerState state = MATE_MIXER_STATE_UNKNOWN; - g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN); - g_object_get (G_OBJECT (backend), - "state", &state, - NULL); + return backend->priv->state; +} - return state; +MateMixerBackendFlags +mate_mixer_backend_get_flags (MateMixerBackend *backend) +{ + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), MATE_MIXER_BACKEND_NO_FLAGS); + + return backend->priv->flags; } -GList * +const GList * mate_mixer_backend_list_devices (MateMixerBackend *backend) { - MateMixerBackendInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + if (backend->priv->devices == NULL) { + MateMixerBackendClass *klass; + + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); - if (iface->list_devices) - return iface->list_devices (backend); + if (klass->list_devices != NULL) + backend->priv->devices = klass->list_devices (backend); + } - return NULL; + return backend->priv->devices; } -GList * +const GList * mate_mixer_backend_list_streams (MateMixerBackend *backend) { - MateMixerBackendInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + if (backend->priv->streams == NULL) { + MateMixerBackendClass *klass; + + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); - if (iface->list_streams) - return iface->list_streams (backend); + if (klass->list_streams != NULL) + backend->priv->streams = klass->list_streams (backend); + } - return NULL; + return backend->priv->streams; } -GList * -mate_mixer_backend_list_cached_streams (MateMixerBackend *backend) +const GList * +mate_mixer_backend_list_stored_streams (MateMixerBackend *backend) { - MateMixerBackendInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + if (backend->priv->stored_streams == NULL) { + MateMixerBackendClass *klass; + + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); - if (iface->list_cached_streams) - return iface->list_cached_streams (backend); + if (klass->list_stored_streams != NULL) + backend->priv->stored_streams = klass->list_stored_streams (backend); + } - return NULL; + return backend->priv->stored_streams; } MateMixerStream * @@ -239,8 +427,8 @@ mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend) g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - g_object_get (G_OBJECT (stream), - "default-input", &stream, + g_object_get (G_OBJECT (backend), + "default-input-stream", &stream, NULL); if (stream != NULL) @@ -253,16 +441,22 @@ gboolean mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *stream) { - MateMixerBackendInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + + if (backend->priv->default_input != stream) { + MateMixerBackendClass *klass; + + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + if (klass->set_default_input_stream == NULL || + klass->set_default_input_stream (backend, stream) == FALSE) + return FALSE; - if (iface->set_default_input_stream) - return iface->set_default_input_stream (backend, stream); + _mate_mixer_backend_set_default_input_stream (backend, stream); + } - return FALSE; + return TRUE; } MateMixerStream * @@ -272,8 +466,8 @@ mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend) g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - g_object_get (G_OBJECT (stream), - "default-output", &stream, + g_object_get (G_OBJECT (backend), + "default-output-stream", &stream, NULL); if (stream != NULL) @@ -286,14 +480,144 @@ gboolean mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *stream) { - MateMixerBackendInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + + if (backend->priv->default_input != stream) { + MateMixerBackendClass *klass; - iface = MATE_MIXER_BACKEND_GET_INTERFACE (backend); + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); + + if (klass->set_default_output_stream == NULL || + klass->set_default_output_stream (backend, stream) == FALSE) + return FALSE; + + _mate_mixer_backend_set_default_output_stream (backend, stream); + } + + return TRUE; +} + +static void +device_added (MateMixerBackend *backend, const gchar *name) +{ + MateMixerDevice *device; + + // device = mate_mixer_backend_get_device (backend, name); + +/* + g_signal_connect (G_OBJECT (device), + "stream-added", + G_CALLBACK (device_stream_added)); + */ +} + +static void +device_removed (MateMixerBackend *backend, const gchar *name) +{ +} + +static void +device_stream_added (MateMixerDevice *device, const gchar *name) +{ + g_signal_emit (G_OBJECT (device), + signals[STREAM_ADDED], + 0, + name); +} + +static void +device_stream_removed (MateMixerDevice *device, const gchar *name) +{ + g_signal_emit (G_OBJECT (device), + signals[STREAM_REMOVED], + 0, + name); +} + +static void +free_devices (MateMixerBackend *backend) +{ + if (backend->priv->devices == NULL) + return; + + g_list_free_full (backend->priv->devices, g_object_unref); + + backend->priv->devices = NULL; +} + +static void +free_streams (MateMixerBackend *backend) +{ + if (backend->priv->streams == NULL) + return; + + g_list_free_full (backend->priv->streams, g_object_unref); + + backend->priv->streams = NULL; +} + +static void +free_stored_streams (MateMixerBackend *backend) +{ + if (backend->priv->stored_streams == NULL) + return; + + g_list_free_full (backend->priv->stored_streams, g_object_unref); + + backend->priv->stored_streams = NULL; +} + +/* Protected */ +void +_mate_mixer_backend_set_state (MateMixerBackend *backend, MateMixerState state) +{ + if (backend->priv->state == state) + return; + + backend->priv->state = state; + + g_object_notify_by_pspec (G_OBJECT (backend), properties[PROP_STATE]); +} + +void +_mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream) +{ + if (backend->priv->default_input == stream) + return; + + if (backend->priv->default_input != NULL) + g_object_unref (backend->priv->default_input); + + if (stream != NULL) + backend->priv->default_input = g_object_ref (stream); + else + backend->priv->default_input = NULL; + + g_debug ("Default input stream changed to %s", + (stream != NULL) ? mate_mixer_stream_get_name (stream) : "none"); + + g_object_notify_by_pspec (G_OBJECT (backend), properties[PROP_DEFAULT_INPUT_STREAM]); +} + +void +_mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream) +{ + if (backend->priv->default_output == stream) + return; + + if (backend->priv->default_output != NULL) + g_object_unref (backend->priv->default_output); + + if (stream != NULL) + backend->priv->default_output = g_object_ref (stream); + else + backend->priv->default_output = NULL; - if (iface->set_default_output_stream) - return iface->set_default_output_stream (backend, stream); + g_debug ("Default output stream changed to %s", + (stream != NULL) ? mate_mixer_stream_get_name (stream) : "none"); - return FALSE; + g_object_notify_by_pspec (G_OBJECT (backend), properties[PROP_DEFAULT_OUTPUT_STREAM]); } diff --git a/libmatemixer/matemixer-backend.h b/libmatemixer/matemixer-backend.h index 8bedfe0..1c918c9 100644 --- a/libmatemixer/matemixer-backend.h +++ b/libmatemixer/matemixer-backend.h @@ -21,94 +21,103 @@ #include #include -#include "matemixer-enums.h" -#include "matemixer-stream.h" +#include +#include G_BEGIN_DECLS -typedef struct -{ - gchar *app_name; - gchar *app_id; - gchar *app_version; - gchar *app_icon; - gchar *server_address; -} MateMixerBackendData; - #define MATE_MIXER_TYPE_BACKEND \ (mate_mixer_backend_get_type ()) #define MATE_MIXER_BACKEND(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_BACKEND, MateMixerBackend)) #define MATE_MIXER_IS_BACKEND(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_BACKEND)) -#define MATE_MIXER_BACKEND_GET_INTERFACE(o) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_BACKEND, MateMixerBackendInterface)) - -typedef struct _MateMixerBackend MateMixerBackend; /* dummy object */ -typedef struct _MateMixerBackendInterface MateMixerBackendInterface; - -struct _MateMixerBackendInterface +#define MATE_MIXER_BACKEND_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_BACKEND, MateMixerBackendClass)) +#define MATE_MIXER_IS_BACKEND_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_BACKEND)) +#define MATE_MIXER_BACKEND_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_BACKEND, MateMixerBackendClass)) + +typedef struct _MateMixerBackend MateMixerBackend; +typedef struct _MateMixerBackendClass MateMixerBackendClass; +typedef struct _MateMixerBackendData MateMixerBackendData; +typedef struct _MateMixerBackendPrivate MateMixerBackendPrivate; + +struct _MateMixerBackend { - GTypeInterface parent_iface; + GObject object; /*< private >*/ - /* Virtual table */ - void (*set_data) (MateMixerBackend *backend, - const MateMixerBackendData *data); + MateMixerBackendPrivate *priv; +}; - gboolean (*open) (MateMixerBackend *backend); - void (*close) (MateMixerBackend *backend); +struct _MateMixerBackendClass +{ + GObjectClass parent_class; - MateMixerState (*get_state) (MateMixerBackend *backend); + /*< private >*/ + void (*set_data) (MateMixerBackend *backend, + MateMixerBackendData *data); - GList *(*list_devices) (MateMixerBackend *backend); - GList *(*list_streams) (MateMixerBackend *backend); - GList *(*list_cached_streams) (MateMixerBackend *backend); + gboolean (*open) (MateMixerBackend *backend); + void (*close) (MateMixerBackend *backend); - MateMixerStream *(*get_default_input_stream) (MateMixerBackend *backend); - gboolean (*set_default_input_stream) (MateMixerBackend *backend, - MateMixerStream *stream); + GList *(*list_devices) (MateMixerBackend *backend); + GList *(*list_streams) (MateMixerBackend *backend); + GList *(*list_stored_streams) (MateMixerBackend *backend); - MateMixerStream *(*get_default_output_stream) (MateMixerBackend *backend); - gboolean (*set_default_output_stream) (MateMixerBackend *backend, - MateMixerStream *stream); + gboolean (*set_default_input_stream) (MateMixerBackend *backend, + MateMixerStream *stream); + gboolean (*set_default_output_stream) (MateMixerBackend *backend, + MateMixerStream *stream); /* Signals */ - void (*device_added) (MateMixerBackend *backend, - const gchar *name); - void (*device_removed) (MateMixerBackend *backend, - const gchar *name); - void (*stream_added) (MateMixerBackend *backend, - const gchar *name); - void (*stream_removed) (MateMixerBackend *backend, - const gchar *name); - void (*cached_stream_added) (MateMixerBackend *backend, - const gchar *name); - void (*cached_stream_removed) (MateMixerBackend *backend, - const gchar *name); + void (*device_added) (MateMixerBackend *backend, + const gchar *name); + void (*device_removed) (MateMixerBackend *backend, + const gchar *name); + void (*stream_added) (MateMixerBackend *backend, + const gchar *name); + void (*stream_removed) (MateMixerBackend *backend, + const gchar *name); + void (*stored_stream_added) (MateMixerBackend *backend, + const gchar *name); + void (*stored_stream_removed) (MateMixerBackend *backend, + const gchar *name); +}; + +struct _MateMixerBackendData +{ + gchar *app_name; + gchar *app_id; + gchar *app_version; + gchar *app_icon; + gchar *server_address; }; -GType mate_mixer_backend_get_type (void) G_GNUC_CONST; +GType mate_mixer_backend_get_type (void) G_GNUC_CONST; -void mate_mixer_backend_set_data (MateMixerBackend *backend, - const MateMixerBackendData *data); +void mate_mixer_backend_set_data (MateMixerBackend *backend, + MateMixerBackendData *data); -gboolean mate_mixer_backend_open (MateMixerBackend *backend); -void mate_mixer_backend_close (MateMixerBackend *backend); +gboolean mate_mixer_backend_open (MateMixerBackend *backend); +void mate_mixer_backend_close (MateMixerBackend *backend); -MateMixerState mate_mixer_backend_get_state (MateMixerBackend *backend); +MateMixerState mate_mixer_backend_get_state (MateMixerBackend *backend); +MateMixerBackendFlags mate_mixer_backend_get_flags (MateMixerBackend *backend); -GList * mate_mixer_backend_list_devices (MateMixerBackend *backend); -GList * mate_mixer_backend_list_streams (MateMixerBackend *backend); -GList * mate_mixer_backend_list_cached_streams (MateMixerBackend *backend); +const GList * mate_mixer_backend_list_devices (MateMixerBackend *backend); +const GList * mate_mixer_backend_list_streams (MateMixerBackend *backend); +const GList * mate_mixer_backend_list_stored_streams (MateMixerBackend *backend); -MateMixerStream *mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend); -gboolean mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, - MateMixerStream *stream); +MateMixerStream * mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend); +gboolean mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream); -MateMixerStream *mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend); -gboolean mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, - MateMixerStream *stream); +MateMixerStream * mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend); +gboolean mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream); G_END_DECLS diff --git a/libmatemixer/matemixer-client-stream.c b/libmatemixer/matemixer-client-stream.c index 3ff3c54..fc34622 100644 --- a/libmatemixer/matemixer-client-stream.c +++ b/libmatemixer/matemixer-client-stream.c @@ -35,68 +35,170 @@ * A typical example of a client stream is a stream provided by an application. */ -G_DEFINE_INTERFACE (MateMixerClientStream, mate_mixer_client_stream, G_TYPE_OBJECT) +struct _MateMixerClientStreamPrivate +{ + gchar *app_name; + gchar *app_id; + gchar *app_version; + gchar *app_icon; + MateMixerStream *parent; + MateMixerClientStreamFlags client_flags; + MateMixerClientStreamRole client_role; +}; + +enum { + PROP_0, + PROP_CLIENT_FLAGS, + PROP_CLIENT_ROLE, + PROP_PARENT, + PROP_APP_NAME, + PROP_APP_ID, + PROP_APP_VERSION, + PROP_APP_ICON, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void mate_mixer_client_stream_class_init (MateMixerClientStreamClass *klass); + +static void mate_mixer_client_stream_init (MateMixerClientStream *client); + +static void mate_mixer_client_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_client_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_client_stream_dispose (GObject *object); +static void mate_mixer_client_stream_finalize (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (MateMixerClientStream, mate_mixer_client_stream, MATE_MIXER_TYPE_STREAM) + +static void +mate_mixer_client_stream_class_init (MateMixerClientStreamClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = mate_mixer_client_stream_dispose; + object_class->finalize = mate_mixer_client_stream_finalize; + object_class->get_property = mate_mixer_client_stream_get_property; + object_class->set_property = mate_mixer_client_stream_set_property; + + properties[PROP_CLIENT_FLAGS] = + g_param_spec_flags ("client-flags", + "Client flags", + "Capability flags of the client stream", + MATE_MIXER_TYPE_CLIENT_STREAM_FLAGS, + MATE_MIXER_CLIENT_STREAM_NO_FLAGS, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_CLIENT_ROLE] = + g_param_spec_enum ("role", + "Role", + "Role of the client stream", + MATE_MIXER_TYPE_CLIENT_STREAM_ROLE, + MATE_MIXER_CLIENT_STREAM_ROLE_NONE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_PARENT] = + g_param_spec_object ("parent", + "Parent", + "Parent stream of the client stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_APP_NAME] = + g_param_spec_string ("app-name", + "App name", + "Name of the client stream application", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_APP_ID] = + g_param_spec_string ("app-id", + "App ID", + "Identifier of the client stream application", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_APP_VERSION] = + g_param_spec_string ("app-version", + "App version", + "Version of the client stream application", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_APP_ICON] = + g_param_spec_string ("app-icon", + "App icon", + "Icon name of the client stream application", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); +} + +static void +mate_mixer_client_stream_init (MateMixerClientStream *client) +{ + client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, + MATE_MIXER_TYPE_CLIENT_STREAM, + MateMixerClientStreamPrivate); +} static void -mate_mixer_client_stream_default_init (MateMixerClientStreamInterface *iface) +mate_mixer_client_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) { - g_object_interface_install_property (iface, - g_param_spec_flags ("client-flags", - "Client flags", - "Capability flags of the client stream", - MATE_MIXER_TYPE_CLIENT_STREAM_FLAGS, - MATE_MIXER_CLIENT_STREAM_NO_FLAGS, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_enum ("role", - "Role", - "Role of the client stream", - MATE_MIXER_TYPE_CLIENT_STREAM_ROLE, - MATE_MIXER_CLIENT_STREAM_ROLE_NONE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_object ("parent", - "Parent", - "Parent stream of the client stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_string ("app-name", - "App name", - "Name of the client stream application", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_string ("app-id", - "App ID", - "Identifier of the client stream application", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_string ("app-version", - "App version", - "Version of the client stream application", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_string ("app-icon", - "App icon", - "Icon name of the client stream application", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); +} + +static void +mate_mixer_client_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ +} + +static void +mate_mixer_client_stream_dispose (GObject *object) +{ + MateMixerClientStream *client; + + client = MATE_MIXER_CLIENT_STREAM (object); + + g_clear_object (&client->priv->parent); + + G_OBJECT_CLASS (mate_mixer_client_stream_parent_class)->dispose (object); +} + +static void +mate_mixer_client_stream_finalize (GObject *object) +{ + MateMixerClientStream *client; + + client = MATE_MIXER_CLIENT_STREAM (object); + + g_free (client->priv->app_name); + g_free (client->priv->app_id); + g_free (client->priv->app_version); + g_free (client->priv->app_icon); + + G_OBJECT_CLASS (mate_mixer_client_stream_parent_class)->finalize (object); } /** @@ -107,16 +209,9 @@ mate_mixer_client_stream_default_init (MateMixerClientStreamInterface *iface) MateMixerClientStreamFlags mate_mixer_client_stream_get_flags (MateMixerClientStream *client) { - MateMixerClientStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_NO_FLAGS); - iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); - - if (iface->get_flags) - return iface->get_flags (client); - - return MATE_MIXER_CLIENT_STREAM_NO_FLAGS; + return client->priv->client_flags; } /** @@ -127,16 +222,9 @@ mate_mixer_client_stream_get_flags (MateMixerClientStream *client) MateMixerClientStreamRole mate_mixer_client_stream_get_role (MateMixerClientStream *client) { - MateMixerClientStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_ROLE_NONE); - iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); - - if (iface->get_role) - return iface->get_role (client); - - return MATE_MIXER_CLIENT_STREAM_ROLE_NONE; + return client->priv->client_role; } /** @@ -150,16 +238,9 @@ mate_mixer_client_stream_get_role (MateMixerClientStream *client) MateMixerStream * mate_mixer_client_stream_get_parent (MateMixerClientStream *client) { - MateMixerClientStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); - - if (iface->get_parent) - return iface->get_parent (client); - - return NULL; + return client->priv->parent; } /** @@ -175,17 +256,25 @@ mate_mixer_client_stream_get_parent (MateMixerClientStream *client) gboolean mate_mixer_client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent) { - MateMixerClientStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), FALSE); g_return_val_if_fail (MATE_MIXER_IS_STREAM (parent), FALSE); - iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + if (client->priv->parent != parent) { + MateMixerClientStreamClass *klass; - if (iface->set_parent) - return iface->set_parent (client, parent); + klass = MATE_MIXER_CLIENT_STREAM_GET_CLASS (client); - return FALSE; + if (klass->set_parent == NULL || + klass->set_parent (client, parent) == FALSE) + return FALSE; + + if (client->priv->parent != NULL) + g_object_unref (client->priv->parent); + + client->priv->parent = g_object_ref (parent); + } + + return TRUE; } /** @@ -199,14 +288,14 @@ mate_mixer_client_stream_set_parent (MateMixerClientStream *client, MateMixerStr gboolean mate_mixer_client_stream_remove (MateMixerClientStream *client) { - MateMixerClientStreamInterface *iface; + MateMixerClientStreamClass *klass; g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), FALSE); - iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); + klass = MATE_MIXER_CLIENT_STREAM_GET_CLASS (client); - if (iface->remove) - return iface->remove (client); + if (klass->remove != NULL) + return klass->remove (client); return FALSE; } @@ -224,16 +313,9 @@ mate_mixer_client_stream_remove (MateMixerClientStream *client) const gchar * mate_mixer_client_stream_get_app_name (MateMixerClientStream *client) { - MateMixerClientStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); - - if (iface->get_app_name) - return iface->get_app_name (client); - - return NULL; + return client->priv->app_name; } /** @@ -249,16 +331,9 @@ mate_mixer_client_stream_get_app_name (MateMixerClientStream *client) const gchar * mate_mixer_client_stream_get_app_id (MateMixerClientStream *client) { - MateMixerClientStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); - - if (iface->get_app_id) - return iface->get_app_id (client); - - return NULL; + return client->priv->app_id; } /** @@ -274,16 +349,9 @@ mate_mixer_client_stream_get_app_id (MateMixerClientStream *client) const gchar * mate_mixer_client_stream_get_app_version (MateMixerClientStream *client) { - MateMixerClientStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); - - if (iface->get_app_version) - return iface->get_app_version (client); - - return NULL; + return client->priv->app_version; } /** @@ -299,14 +367,7 @@ mate_mixer_client_stream_get_app_version (MateMixerClientStream *client) const gchar * mate_mixer_client_stream_get_app_icon (MateMixerClientStream *client) { - MateMixerClientStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - iface = MATE_MIXER_CLIENT_STREAM_GET_INTERFACE (client); - - if (iface->get_app_icon) - return iface->get_app_icon (client); - - return NULL; + return client->priv->app_icon; } diff --git a/libmatemixer/matemixer-client-stream.h b/libmatemixer/matemixer-client-stream.h index fae5934..43ab3f0 100644 --- a/libmatemixer/matemixer-client-stream.h +++ b/libmatemixer/matemixer-client-stream.h @@ -22,28 +22,39 @@ #include #include -#include +#include G_BEGIN_DECLS -#define MATE_MIXER_TYPE_CLIENT_STREAM \ +#define MATE_MIXER_TYPE_CLIENT_STREAM \ (mate_mixer_client_stream_get_type ()) -#define MATE_MIXER_CLIENT_STREAM(o) \ +#define MATE_MIXER_CLIENT_STREAM(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_CLIENT_STREAM, MateMixerClientStream)) -#define MATE_MIXER_IS_CLIENT_STREAM(o) \ +#define MATE_MIXER_IS_CLIENT_STREAM(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_CLIENT_STREAM)) -#define MATE_MIXER_CLIENT_STREAM_GET_INTERFACE(o) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_CLIENT_STREAM, MateMixerClientStreamInterface)) +#define MATE_MIXER_CLIENT_STREAM_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_CLIENT_STREAM, MateMixerClientStreamClass)) +#define MATE_MIXER_IS_CLIENT_STREAM_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_CLIENT_STREAM)) +#define MATE_MIXER_CLIENT_STREAM_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_CLIENT_STREAM, MateMixerClientStreamClass)) -typedef struct _MateMixerClientStream MateMixerClientStream; /* dummy object */ -typedef struct _MateMixerClientStreamInterface MateMixerClientStreamInterface; +typedef struct _MateMixerClientStreamClass MateMixerClientStreamClass; +typedef struct _MateMixerClientStreamPrivate MateMixerClientStreamPrivate; -struct _MateMixerClientStreamInterface +struct _MateMixerClientStream +{ + GObject *parent; + + /*< private >*/ + MateMixerClientStreamPrivate *priv; +}; + +struct _MateMixerClientStreamClass { GTypeInterface parent_iface; /*< private >*/ - /* Virtual table */ MateMixerClientStreamFlags (*get_flags) (MateMixerClientStream *client); MateMixerClientStreamRole (*get_role) (MateMixerClientStream *client); diff --git a/libmatemixer/matemixer-context.c b/libmatemixer/matemixer-context.c new file mode 100644 index 0000000..5bdc7a8 --- /dev/null +++ b/libmatemixer/matemixer-context.c @@ -0,0 +1,1393 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include "matemixer.h" +#include "matemixer-backend.h" +#include "matemixer-backend-module.h" +#include "matemixer-client-stream.h" +#include "matemixer-context.h" +#include "matemixer-enums.h" +#include "matemixer-enum-types.h" +#include "matemixer-private.h" +#include "matemixer-stream.h" + +/** + * SECTION:matemixer-context + * @short_description:The main class for interfacing with the library + * @include: libmatemixer/matemixer.h + */ + +struct _MateMixerContextPrivate +{ + gboolean backend_chosen; + MateMixerState state; + MateMixerBackend *backend; + MateMixerBackendData backend_data; + MateMixerBackendType backend_type; + MateMixerBackendModule *module; +}; + +enum { + PROP_0, + PROP_APP_NAME, + PROP_APP_ID, + PROP_APP_VERSION, + PROP_APP_ICON, + PROP_SERVER_ADDRESS, + PROP_STATE, + PROP_DEFAULT_INPUT_STREAM, + PROP_DEFAULT_OUTPUT_STREAM, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + STREAM_ADDED, + STREAM_REMOVED, + STORED_STREAM_ADDED, + STORED_STREAM_REMOVED, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0, }; + +static void mate_mixer_context_class_init (MateMixerContextClass *klass); + +static void mate_mixer_context_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_context_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_context_init (MateMixerContext *context); +static void mate_mixer_context_dispose (GObject *object); +static void mate_mixer_context_finalize (GObject *object); + +G_DEFINE_TYPE (MateMixerContext, mate_mixer_context, G_TYPE_OBJECT); + +static void on_backend_state_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerContext *context); + +static void on_backend_device_added (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context); +static void on_backend_device_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context); + +static void on_backend_stream_added (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context); +static void on_backend_stream_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context); + +static void on_backend_stored_stream_added (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context); +static void on_backend_stored_stream_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context); + +static void on_backend_default_input_stream_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerContext *context); +static void on_backend_default_output_stream_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerContext *context); + +static gboolean try_next_backend (MateMixerContext *context); + +static void change_state (MateMixerContext *context, + MateMixerState state); + +static void close_context (MateMixerContext *context); + +static void +mate_mixer_context_class_init (MateMixerContextClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = mate_mixer_context_dispose; + object_class->finalize = mate_mixer_context_finalize; + object_class->get_property = mate_mixer_context_get_property; + object_class->set_property = mate_mixer_context_set_property; + + /** + * MateMixerContext:app-name: + * + * Localized human readable name of the application. + */ + properties[PROP_APP_NAME] = + g_param_spec_string ("app-name", + "App name", + "Application name", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * MateMixerContext:app-id: + * + * Identifier of the application (e.g. org.example.app). + */ + properties[PROP_APP_ID] = + g_param_spec_string ("app-id", + "App ID", + "Application identifier", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * MateMixerContext:app-version: + * + * Version of the application. + */ + properties[PROP_APP_VERSION] = + g_param_spec_string ("app-version", + "App version", + "Application version", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * MateMixerContext:app-icon: + * + * An XDG icon name for the application. + */ + properties[PROP_APP_ICON] = + g_param_spec_string ("app-icon", + "App icon", + "Application icon name", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * MateMixerContext:server-address: + * + * Address of the sound server to connect to. + * + * This feature is only supported in the PulseAudio backend. There is + * no need to specify an address in order to connect to the local daemon. + */ + properties[PROP_SERVER_ADDRESS] = + g_param_spec_string ("server-address", + "Server address", + "Sound server address", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE] = + g_param_spec_enum ("state", + "State", + "Current backend connection state", + MATE_MIXER_TYPE_STATE, + MATE_MIXER_STATE_IDLE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + properties[PROP_DEFAULT_INPUT_STREAM] = + g_param_spec_object ("default-input-stream", + "Default input stream", + "Default input stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + properties[PROP_DEFAULT_OUTPUT_STREAM] = + g_param_spec_object ("default-output-stream", + "Default output stream", + "Default output stream", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + /** + * MateMixerContext::device-added: + * @context: a #MateMixerContext + * @name: name of the added device + * + * The signal is emitted each time a device is added to the system. + */ + signals[DEVICE_ADDED] = + g_signal_new ("device-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerContextClass, device_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerContext::device-removed: + * @context: a #MateMixerContext + * @name: name of the removed device + * + * The signal is emitted each time a device is removed from the system. + */ + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerContextClass, device_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerContext::stream-added: + * @context: a #MateMixerContext + * @name: name of the added stream + * + * The signal is emitted each time a stream is created. + */ + signals[STREAM_ADDED] = + g_signal_new ("stream-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerContextClass, stream_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerContext::stream-removed: + * @context: a #MateMixerContext + * @name: name of the removed stream + * + * The signal is emitted each time a stream is removed. + */ + signals[STREAM_REMOVED] = + g_signal_new ("stream-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerContextClass, stream_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerContext::stored-stream-added: + * @context: a #MateMixerContext + * @name: name of the added stored stream + * + * The signal is emitted each time a stored stream is created. + */ + signals[STORED_STREAM_ADDED] = + g_signal_new ("stored-control-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerContextClass, stored_stream_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * MateMixerContext::stream-removed: + * @context: a #MateMixerContext + * @name: name of the removed stream + * + * The signal is emitted each time a stream is removed. + */ + signals[STORED_STREAM_REMOVED] = + g_signal_new ("stored-stream-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerContextClass, stored_stream_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + g_type_class_add_private (object_class, sizeof (MateMixerContextPrivate)); +} + +static void +mate_mixer_context_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerContext *context; + + context = MATE_MIXER_CONTEXT (object); + + switch (param_id) { + case PROP_APP_NAME: + g_value_set_string (value, context->priv->backend_data.app_name); + break; + case PROP_APP_ID: + g_value_set_string (value, context->priv->backend_data.app_id); + break; + case PROP_APP_VERSION: + g_value_set_string (value, context->priv->backend_data.app_version); + break; + case PROP_APP_ICON: + g_value_set_string (value, context->priv->backend_data.app_icon); + break; + case PROP_SERVER_ADDRESS: + g_value_set_string (value, context->priv->backend_data.server_address); + break; + case PROP_STATE: + g_value_set_enum (value, context->priv->state); + break; + case PROP_DEFAULT_INPUT_STREAM: + g_value_set_object (value, mate_mixer_context_get_default_input_stream (context)); + break; + case PROP_DEFAULT_OUTPUT_STREAM: + g_value_set_object (value, mate_mixer_context_get_default_output_stream (context)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_context_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerContext *context; + + context = MATE_MIXER_CONTEXT (object); + + switch (param_id) { + case PROP_APP_NAME: + mate_mixer_context_set_app_name (context, g_value_get_string (value)); + break; + case PROP_APP_ID: + mate_mixer_context_set_app_id (context, g_value_get_string (value)); + break; + case PROP_APP_VERSION: + mate_mixer_context_set_app_version (context, g_value_get_string (value)); + break; + case PROP_APP_ICON: + mate_mixer_context_set_app_icon (context, g_value_get_string (value)); + break; + case PROP_SERVER_ADDRESS: + mate_mixer_context_set_server_address (context, g_value_get_string (value)); + break; + case PROP_DEFAULT_INPUT_STREAM: + mate_mixer_context_set_default_input_stream (context, g_value_get_object (value)); + break; + case PROP_DEFAULT_OUTPUT_STREAM: + mate_mixer_context_set_default_output_stream (context, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_context_init (MateMixerContext *context) +{ + context->priv = G_TYPE_INSTANCE_GET_PRIVATE (context, + MATE_MIXER_TYPE_CONTEXT, + MateMixerContextPrivate); +} + +static void +mate_mixer_context_dispose (GObject *object) +{ + close_context (MATE_MIXER_CONTEXT (object)); + + G_OBJECT_CLASS (mate_mixer_context_parent_class)->dispose (object); +} + +static void +mate_mixer_context_finalize (GObject *object) +{ + MateMixerContext *context; + + context = MATE_MIXER_CONTEXT (object); + + g_free (context->priv->backend_data.app_name); + g_free (context->priv->backend_data.app_id); + g_free (context->priv->backend_data.app_version); + g_free (context->priv->backend_data.app_icon); + g_free (context->priv->backend_data.server_address); + + G_OBJECT_CLASS (mate_mixer_context_parent_class)->finalize (object); +} + +/** + * mate_mixer_context_new: + * + * Creates a new #MateMixerContext instance. + * + * Returns: a new #MateMixerContext instance or %NULL if the library has not + * been initialized using mate_mixer_init(). + */ +MateMixerContext * +mate_mixer_context_new (void) +{ + if (mate_mixer_is_initialized () == FALSE) { + g_critical ("The library has not been initialized"); + return NULL; + } + + return g_object_new (MATE_MIXER_TYPE_CONTEXT, NULL); +} + +/** + * mate_mixer_context_set_backend_type: + * @context: a #MateMixerContext + * @backend_type: the sound system backend to use + * + * Makes the #MateMixerContext use the given #MateMixerBackendType. + * + * By default the backend type is determined automatically. This function can + * be used before mate_mixer_context_open() to alter this behavior and make the + * @context use the given backend. + * + * This function will fail if support for the backend is not installed in + * the system or if the current state is either %MATE_MIXER_STATE_CONNECTING or + * %MATE_MIXER_STATE_READY. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_context_set_backend_type (MateMixerContext *context, + MateMixerBackendType backend_type) +{ + MateMixerBackendModule *module; + const GList *modules; + const MateMixerBackendInfo *info; + + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE); + + if (context->priv->state == MATE_MIXER_STATE_CONNECTING || + context->priv->state == MATE_MIXER_STATE_READY) + return FALSE; + + modules = _mate_mixer_get_modules (); + + while (modules != NULL) { + module = MATE_MIXER_BACKEND_MODULE (modules->data); + info = mate_mixer_backend_module_get_info (module); + + if (info->backend_type == backend_type) { + context->priv->backend_type = backend_type; + return TRUE; + } + modules = modules->next; + } + return FALSE; +} + +/** + * mate_mixer_context_set_app_name: + * @context: a #MateMixerContext + * @app_name: the name of your application, or %NULL to unset + * + * Sets the name of the application. This feature is only supported in the + * PulseAudio backend. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_context_set_app_name (MateMixerContext *context, const gchar *app_name) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE); + + if (context->priv->state == MATE_MIXER_STATE_CONNECTING || + context->priv->state == MATE_MIXER_STATE_READY) + return FALSE; + + if (g_strcmp0 (context->priv->backend_data.app_name, app_name) != 0) { + g_free (context->priv->backend_data.app_name); + + context->priv->backend_data.app_name = g_strdup (app_name); + + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_NAME]); + } + return TRUE; +} + +/** + * mate_mixer_context_set_app_id: + * @context: a #MateMixerContext + * @app_id: the identifier of your application, or %NULL to unset + * + * Sets the identifier of the application (e.g. org.example.app). This feature + * is only supported in the PulseAudio backend. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_context_set_app_id (MateMixerContext *context, const gchar *app_id) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE); + + if (context->priv->state == MATE_MIXER_STATE_CONNECTING || + context->priv->state == MATE_MIXER_STATE_READY) + return FALSE; + + if (g_strcmp0 (context->priv->backend_data.app_id, app_id) != 0) { + g_free (context->priv->backend_data.app_id); + + context->priv->backend_data.app_id = g_strdup (app_id); + + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_ID]); + } + return TRUE; +} + +/** + * mate_mixer_context_set_app_version: + * @context: a #MateMixerContext + * @app_version: the version of your application, or %NULL to unset + * + * Sets the version of the application. This feature is only supported in the + * PulseAudio backend. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_context_set_app_version (MateMixerContext *context, const gchar *app_version) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE); + + if (context->priv->state == MATE_MIXER_STATE_CONNECTING || + context->priv->state == MATE_MIXER_STATE_READY) + return FALSE; + + if (g_strcmp0 (context->priv->backend_data.app_version, app_version) != 0) { + g_free (context->priv->backend_data.app_version); + + context->priv->backend_data.app_version = g_strdup (app_version); + + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_VERSION]); + } + return TRUE; +} + +/** + * mate_mixer_context_set_app_icon: + * @context: a #MateMixerContext + * @app_icon: the XDG icon name of your application, or %NULL to unset + * + * Sets the XDG icon name of the application. This feature is only supported in + * the PulseAudio backend. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_context_set_app_icon (MateMixerContext *context, const gchar *app_icon) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE); + + if (context->priv->state == MATE_MIXER_STATE_CONNECTING || + context->priv->state == MATE_MIXER_STATE_READY) + return FALSE; + + if (g_strcmp0 (context->priv->backend_data.app_icon, app_icon) != 0) { + g_free (context->priv->backend_data.app_icon); + + context->priv->backend_data.app_icon = g_strdup (app_icon); + + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_ICON]); + } + return TRUE; +} + +/** + * mate_mixer_context_set_server_address: + * @context: a #MateMixerContext + * @address: the address of the sound server to connect to or %NULL + * + * Sets the address of the sound server. This feature is only supported in the + * PulseAudio backend. If the address is not set, the default PulseAudio sound + * server will be used, which is normally the local daemon. + * + * This function will fail if the current state is either + * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should + * use it before opening a connection to the sound system. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_context_set_server_address (MateMixerContext *context, const gchar *address) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE); + + if (context->priv->state == MATE_MIXER_STATE_CONNECTING || + context->priv->state == MATE_MIXER_STATE_READY) + return FALSE; + + if (g_strcmp0 (context->priv->backend_data.server_address, address) != 0) { + g_free (context->priv->backend_data.server_address); + + context->priv->backend_data.server_address = g_strdup (address); + + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_SERVER_ADDRESS]); + } + return TRUE; +} + +/** + * mate_mixer_context_open: + * @context: a #MateMixerContext + * + * Opens connection to a sound system. Unless the backend type was given + * beforehand, the library will find a working sound system automatically. + * If the automatic discovery fails to find a working sound system, it will + * use the "Null" backend, which provides no functionality. + * + * This function can complete the operation either synchronously or + * asynchronously. + * + * In case this function returns %TRUE, you should check the current state of + * the connection using mate_mixer_context_get_state(). If the current state + * is %MATE_MIXER_STATE_READY, the connection has been established successfully. + * Otherwise the state will be set to %MATE_MIXER_STATE_CONNECTING and the + * result of the operation will be determined asynchronously. You should wait + * for the state transition by connecting to the notification signal of the + * #MateMixerContext:state property. + * + * In case this function returns %FALSE, it is not possible to use the selected + * backend and the state will be set to %MATE_MIXER_STATE_FAILED. + * + * Returns: %TRUE on success or if the result will be determined asynchronously, + * or %FALSE on failure. + */ +gboolean +mate_mixer_context_open (MateMixerContext *context) +{ + MateMixerBackendModule *module = NULL; + MateMixerState state; + const GList *modules; + const MateMixerBackendInfo *info = NULL; + + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE); + + if (context->priv->state == MATE_MIXER_STATE_CONNECTING || + context->priv->state == MATE_MIXER_STATE_READY) + return FALSE; + + /* We are going to choose the first backend to try. It will be either the one + * specified by the application or the one with the highest priority */ + modules = _mate_mixer_get_modules (); + + if (context->priv->backend_type != MATE_MIXER_BACKEND_UNKNOWN) { + while (modules != NULL) { + const MateMixerBackendInfo *info; + + module = MATE_MIXER_BACKEND_MODULE (modules->data); + info = mate_mixer_backend_module_get_info (module); + + if (info->backend_type == context->priv->backend_type) + break; + + module = NULL; + modules = modules->next; + } + } else { + /* The highest priority module is on the top of the list */ + module = MATE_MIXER_BACKEND_MODULE (modules->data); + } + if (module == NULL) { + /* Most likely the selected backend is not installed */ + change_state (context, MATE_MIXER_STATE_FAILED); + return FALSE; + } + + if (info == NULL) + info = mate_mixer_backend_module_get_info (module); + + context->priv->module = g_object_ref (module); + context->priv->backend = g_object_new (info->g_type, NULL); + + mate_mixer_backend_set_data (context->priv->backend, &context->priv->backend_data); + + /* This transitional state is always present, it will change to MATE_MIXER_STATE_READY + * or MATE_MIXER_STATE_FAILED either instantly or asynchronously */ + change_state (context, MATE_MIXER_STATE_CONNECTING); + + /* The backend initialization might fail in case it is known right now that + * the backend is unusable */ + if (mate_mixer_backend_open (context->priv->backend) == FALSE) { + if (context->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) { + /* User didn't request a specific backend, so try another one */ + return try_next_backend (context); + } + + /* User requested a specific backend and it failed */ + close_context (context); + change_state (context, MATE_MIXER_STATE_FAILED); + return FALSE; + } + + state = mate_mixer_backend_get_state (context->priv->backend); + + if (G_UNLIKELY (state != MATE_MIXER_STATE_READY && + state != MATE_MIXER_STATE_CONNECTING)) { + /* This would be a backend bug */ + g_warn_if_reached (); + + if (context->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) + return try_next_backend (context); + + close_context (context); + change_state (context, MATE_MIXER_STATE_FAILED); + return FALSE; + } + + g_signal_connect (G_OBJECT (context->priv->backend), + "notify::state", + G_CALLBACK (on_backend_state_notify), + context); + + change_state (context, state); + return TRUE; +} + +/** + * mate_mixer_context_close: + * @context: a #MateMixerContext + * + * Closes connection to the currently used sound system. The state will be + * set to %MATE_MIXER_STATE_IDLE. + */ +void +mate_mixer_context_close (MateMixerContext *context) +{ + g_return_if_fail (MATE_MIXER_IS_CONTEXT (context)); + + close_context (context); + change_state (context, MATE_MIXER_STATE_IDLE); +} + +/** + * mate_mixer_context_get_state: + * @context: a #MateMixerContext + * + * Gets the current backend connection state of the #MateMixerContext. + * + * Returns: The current connection state. + */ +MateMixerState +mate_mixer_context_get_state (MateMixerContext *context) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), MATE_MIXER_STATE_UNKNOWN); + + return context->priv->state; +} + +/** + * mate_mixer_context_get_device: + * @context: a #MateMixerContext + * @name: a device name + * + * Gets the device with the given name. + * + * Returns: a #MateMixerDevice or %NULL if there is no such device. + */ +MateMixerDevice * +mate_mixer_context_get_device (MateMixerContext *context, const gchar *name) +{ + GList *list; + + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); + g_return_val_if_fail (name != NULL, NULL); + + list = (GList *) mate_mixer_context_list_devices (context); + while (list != NULL) { + MateMixerDevice *device = MATE_MIXER_DEVICE (list->data); + + if (strcmp (name, mate_mixer_device_get_name (device)) == 0) + return device; + + list = list->next; + } + return NULL; +} + +/** + * mate_mixer_context_get_stream: + * @context: a #MateMixerContext + * @name: a stream name + * + * Gets the stream with the given name. + * + * Returns: a #MateMixerStream or %NULL if there is no such stream. + */ +MateMixerStream * +mate_mixer_context_get_stream (MateMixerContext *context, const gchar *name) +{ + GList *list; + + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); + g_return_val_if_fail (name != NULL, NULL); + + list = (GList *) mate_mixer_context_list_streams (context); + while (list != NULL) { + MateMixerStream *stream = MATE_MIXER_STREAM (list->data); + + if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) + return stream; + + list = list->next; + } + return NULL; +} + +/** + * mate_mixer_context_get_stored_stream: + * @context: a #MateMixerContext + * @name: a stream name + * + * Gets the stream with the given name. + * + * Returns: a #MateMixerStream or %NULL if there is no such stream. + */ +MateMixerStream * +mate_mixer_context_get_stored_stream (MateMixerContext *context, const gchar *name) +{ + GList *list; + + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); + g_return_val_if_fail (name != NULL, NULL); + + list = (GList *) mate_mixer_context_list_stored_streams (context); + while (list != NULL) { + MateMixerStream *stream = MATE_MIXER_STREAM (list->data); + + if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) + return stream; + + list = list->next; + } + return NULL; +} + +/** + * mate_mixer_context_list_devices: + * @context: a #MateMixerContext + * + * Gets a list of devices. Each list item is a #MateMixerDevice representing a + * hardware or software sound device in the system. + * + * The returned #GList is owned by the #MateMixerContext and may be invalidated + * at any time. + * + * Returns: a #GList of all devices in the system or %NULL if there are none or + * you are not connected to a sound system. + */ +const GList * +mate_mixer_context_list_devices (MateMixerContext *context) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); + + if (context->priv->state != MATE_MIXER_STATE_READY) + return NULL; + + return mate_mixer_backend_list_devices (MATE_MIXER_BACKEND (context->priv->backend)); +} + +/** + * mate_mixer_context_list_streams: + * @context: a #MateMixerContext + * + * Gets a list of streams. Each list item is a #MateMixerStream representing an + * input or output of a sound device. + * + * The returned #GList is owned by the #MateMixerContext and may be invalidated + * at any time. + * + * Returns: a #GList of all streams in the system or %NULL if there are none or + * you are not connected to a sound system. + */ +const GList * +mate_mixer_context_list_streams (MateMixerContext *context) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); + + if (context->priv->state != MATE_MIXER_STATE_READY) + return NULL; + + return mate_mixer_backend_list_streams (MATE_MIXER_BACKEND (context->priv->backend)); +} + +/** + * mate_mixer_context_list_stored_streams: + * @context: a #MateMixerContext + * + */ +const GList * +mate_mixer_context_list_stored_streams (MateMixerContext *context) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); + + if (context->priv->state != MATE_MIXER_STATE_READY) + return NULL; + + return mate_mixer_backend_list_stored_streams (MATE_MIXER_BACKEND (context->priv->backend)); +} + +/** + * mate_mixer_context_get_default_input_stream: + * @context: a #MateMixerContext + * + * Gets the default input stream. The returned stream is where sound input is + * directed to by default. + * + * Returns: a #MateMixerStream or %NULL if there are no input streams in + * the system. + */ +MateMixerStream * +mate_mixer_context_get_default_input_stream (MateMixerContext *context) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); + + if (context->priv->state != MATE_MIXER_STATE_READY) + return NULL; + + return mate_mixer_backend_get_default_input_stream (context->priv->backend); +} + +/** + * mate_mixer_context_set_default_input_stream: + * @context: a #MateMixerContext + * @stream: a #MateMixerStream to set as the default input stream + * + * Changes the default input stream in the system. The @stream must be an + * input non-client stream. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_context_set_default_input_stream (MateMixerContext *context, + MateMixerStream *stream) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + + if (context->priv->state != MATE_MIXER_STATE_READY) + return FALSE; + + if (MATE_MIXER_IS_CLIENT_STREAM (stream)) { + g_warning ("Unable to set client stream as the default input stream"); + return FALSE; + } + if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_INPUT)) { + g_warning ("Unable to set non-input stream as the default input stream"); + return FALSE; + } + + return mate_mixer_backend_set_default_input_stream (context->priv->backend, stream); +} + +/** + * mate_mixer_context_get_default_output_stream: + * @context: a #MateMixerContext + * + * Gets the default output stream. The returned stream is where sound output is + * directed to by default. + * + * Returns: a #MateMixerStream or %NULL if there are no output streams in + * the system. + */ +MateMixerStream * +mate_mixer_context_get_default_output_stream (MateMixerContext *context) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); + + if (context->priv->state != MATE_MIXER_STATE_READY) + return NULL; + + return mate_mixer_backend_get_default_output_stream (context->priv->backend); +} + +/** + * mate_mixer_context_set_default_output_stream: + * @context: a #MateMixerContext + * @stream: a #MateMixerStream to set as the default output stream + * + * Changes the default output stream in the system. The @stream must be an + * output non-client stream. + * + * Returns: %TRUE on success or %FALSE on failure. + */ +gboolean +mate_mixer_context_set_default_output_stream (MateMixerContext *context, + MateMixerStream *stream) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + + if (context->priv->state != MATE_MIXER_STATE_READY) + return FALSE; + + if (MATE_MIXER_IS_CLIENT_STREAM (stream)) { + g_warning ("Unable to set client stream as the default output stream"); + return FALSE; + } + if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_OUTPUT)) { + g_warning ("Unable to set non-output stream as the default output stream"); + return FALSE; + } + + return mate_mixer_backend_set_default_output_stream (context->priv->backend, stream); +} + +/** + * mate_mixer_context_get_backend_name: + * @context: a #MateMixerContext + * + * Gets the name of the currently used backend. This function will not + * work until connected to a sound system. + * + * Returns: the name or %NULL on error. + */ +const gchar * +mate_mixer_context_get_backend_name (MateMixerContext *context) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); + + if (context->priv->backend_chosen == FALSE) + return NULL; + + return mate_mixer_backend_module_get_info (context->priv->module)->name; +} + +/** + * mate_mixer_context_get_backend_type: + * @context: a #MateMixerContext + * + * Gets the type of the currently used backend. This function will not + * work until connected to a sound system. + * + * Returns: the backend type or %MATE_MIXER_BACKEND_UNKNOWN on error. + */ +MateMixerBackendType +mate_mixer_context_get_backend_type (MateMixerContext *context) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), MATE_MIXER_BACKEND_UNKNOWN); + + if (context->priv->backend_chosen == FALSE) + return MATE_MIXER_BACKEND_UNKNOWN; + + return mate_mixer_backend_module_get_info (context->priv->module)->backend_type; +} + +/** + * mate_mixer_context_get_backend_flags: + * @context: a #MateMixerContext + */ +MateMixerBackendFlags +mate_mixer_context_get_backend_flags (MateMixerContext *context) +{ + g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), MATE_MIXER_BACKEND_NO_FLAGS); + + return mate_mixer_backend_get_flags (context->priv->backend); +} + +static void +on_backend_state_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerContext *context) +{ + MateMixerState state = mate_mixer_backend_get_state (backend); + + switch (state) { + case MATE_MIXER_STATE_CONNECTING: + g_debug ("Backend %s changed state to CONNECTING", + mate_mixer_backend_module_get_info (context->priv->module)->name); + + change_state (context, state); + break; + + case MATE_MIXER_STATE_READY: + g_debug ("Backend %s changed state to READY", + mate_mixer_backend_module_get_info (context->priv->module)->name); + + change_state (context, state); + break; + + case MATE_MIXER_STATE_FAILED: + g_debug ("Backend %s changed state to FAILED", + mate_mixer_backend_module_get_info (context->priv->module)->name); + + if (context->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) { + /* User didn't request a specific backend, so try another one */ + try_next_backend (context); + } else { + /* User requested a specific backend and it failed */ + close_context (context); + change_state (context, state); + } + break; + + default: + break; + } +} + +static void +on_backend_device_added (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context) +{ + g_signal_emit (G_OBJECT (context), + signals[DEVICE_ADDED], + 0, + name); +} + +static void +on_backend_device_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context) +{ + g_signal_emit (G_OBJECT (context), + signals[DEVICE_REMOVED], + 0, + name); +} + +static void +on_backend_stream_added (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context) +{ + g_signal_emit (G_OBJECT (context), + signals[STREAM_ADDED], + 0, + name); +} + +static void +on_backend_stream_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context) +{ + g_signal_emit (G_OBJECT (context), + signals[STREAM_REMOVED], + 0, + name); +} + +static void +on_backend_stored_stream_added (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context) +{ + g_signal_emit (G_OBJECT (context), + signals[STORED_STREAM_ADDED], + 0, + name); +} + +static void +on_backend_stored_stream_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context) +{ + g_signal_emit (G_OBJECT (context), + signals[STORED_STREAM_REMOVED], + 0, + name); +} + +static void +on_backend_default_input_stream_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerContext *context) +{ + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_DEFAULT_INPUT_STREAM]); +} + +static void +on_backend_default_output_stream_notify (MateMixerBackend *backend, + GParamSpec *pspec, + MateMixerContext *context) +{ + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_DEFAULT_OUTPUT_STREAM]); +} + +static gboolean +try_next_backend (MateMixerContext *context) +{ + const GList *modules; + MateMixerBackendModule *module = NULL; + MateMixerState state; + + modules = _mate_mixer_get_modules (); + + while (modules != NULL) { + if (context->priv->module == modules->data) { + /* Found the last tested backend, try to use the next one with a lower + * priority unless we have reached the end of the list */ + if (modules->next != NULL) + module = MATE_MIXER_BACKEND_MODULE (modules->next->data); + break; + } + modules = modules->next; + } + close_context (context); + + if (module == NULL) { + /* We have tried all the modules and all of them failed */ + change_state (context, MATE_MIXER_STATE_FAILED); + return FALSE; + } + + context->priv->module = g_object_ref (module); + context->priv->backend = + g_object_new (mate_mixer_backend_module_get_info (module)->g_type, NULL); + + mate_mixer_backend_set_data (context->priv->backend, &context->priv->backend_data); + + /* Try to open this backend and in case of failure keep trying until we find + * one that works or reach the end of the list */ + if (mate_mixer_backend_open (context->priv->backend) == FALSE) + return try_next_backend (context); + + state = mate_mixer_backend_get_state (context->priv->backend); + + if (G_UNLIKELY (state != MATE_MIXER_STATE_READY && + state != MATE_MIXER_STATE_CONNECTING)) { + /* This would be a backend bug */ + g_warn_if_reached (); + + return try_next_backend (context); + } + + g_signal_connect (G_OBJECT (context->priv->backend), + "notify::state", + G_CALLBACK (on_backend_state_notify), + context); + + change_state (context, state); + return TRUE; +} + +static void +change_state (MateMixerContext *context, MateMixerState state) +{ + if (context->priv->state == state) + return; + + context->priv->state = state; + + if (state == MATE_MIXER_STATE_READY && context->priv->backend_chosen == FALSE) { + /* It is safe to connect to the backend signals after reaching the READY + * state, because the app is not allowed to query any data before that state; + * therefore we won't end up in an inconsistent state by caching a list and + * then missing a notification about a change in the list */ + g_signal_connect (G_OBJECT (context->priv->backend), + "device-added", + G_CALLBACK (on_backend_device_added), + context); + g_signal_connect (G_OBJECT (context->priv->backend), + "device-removed", + G_CALLBACK (on_backend_device_removed), + context); + g_signal_connect (G_OBJECT (context->priv->backend), + "stream-added", + G_CALLBACK (on_backend_stream_added), + context); + g_signal_connect (G_OBJECT (context->priv->backend), + "stream-removed", + G_CALLBACK (on_backend_stream_removed), + context); + g_signal_connect (G_OBJECT (context->priv->backend), + "stored-stream-added", + G_CALLBACK (on_backend_stored_stream_added), + context); + g_signal_connect (G_OBJECT (context->priv->backend), + "stored-stream-removed", + G_CALLBACK (on_backend_stored_stream_removed), + context); + + g_signal_connect (G_OBJECT (context->priv->backend), + "notify::default-input", + G_CALLBACK (on_backend_default_input_stream_notify), + context); + g_signal_connect (G_OBJECT (context->priv->backend), + "notify::default-output", + G_CALLBACK (on_backend_default_output_stream_notify), + context); + + context->priv->backend_chosen = TRUE; + } + + g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_STATE]); +} + +static void +close_context (MateMixerContext *context) +{ + if (context->priv->backend != NULL) { + g_signal_handlers_disconnect_by_data (G_OBJECT (context->priv->backend), + context); + + mate_mixer_backend_close (context->priv->backend); + g_clear_object (&context->priv->backend); + } + + g_clear_object (&context->priv->module); + + context->priv->backend_chosen = FALSE; +} diff --git a/libmatemixer/matemixer-context.h b/libmatemixer/matemixer-context.h new file mode 100644 index 0000000..3370570 --- /dev/null +++ b/libmatemixer/matemixer-context.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_CONTEXT_H +#define MATEMIXER_CONTEXT_H + +#include +#include + +#include +#include + +G_BEGIN_DECLS + +#define MATE_MIXER_TYPE_CONTEXT \ + (mate_mixer_context_get_type ()) +#define MATE_MIXER_CONTEXT(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_CONTEXT, MateMixerContext)) +#define MATE_MIXER_IS_CONTEXT(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_CONTEXT)) +#define MATE_MIXER_CONTEXT_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_CONTEXT, MateMixerContextClass)) +#define MATE_MIXER_IS_CONTEXT_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_CONTEXT)) +#define MATE_MIXER_CONTEXT_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_CONTEXT, MateMixerContextClass)) + +typedef struct _MateMixerContextClass MateMixerContextClass; +typedef struct _MateMixerContextPrivate MateMixerContextPrivate; + +/** + * MateMixerContext: + * + * The #MateMixerContext structure contains only private data and should only + * be accessed using the provided API. + */ +struct _MateMixerContext +{ + GObject parent; + + /*< private >*/ + MateMixerContextPrivate *priv; +}; + +/** + * MateMixerContextClass: + * + * The class structure of #MateMixerContext. + */ +struct _MateMixerContextClass +{ + GObjectClass parent_class; + + /*< private >*/ + void (*device_added) (MateMixerContext *context, + const gchar *name); + void (*device_removed) (MateMixerContext *context, + const gchar *name); + void (*stream_added) (MateMixerContext *context, + const gchar *name); + void (*stream_removed) (MateMixerContext *context, + const gchar *name); + void (*stored_stream_added) (MateMixerContext *context, + const gchar *name); + void (*stored_stream_removed) (MateMixerContext *context, + const gchar *name); +}; + +GType mate_mixer_context_get_type (void) G_GNUC_CONST; + +MateMixerContext * mate_mixer_context_new (void); + +gboolean mate_mixer_context_set_backend_type (MateMixerContext *context, + MateMixerBackendType backend_type); +gboolean mate_mixer_context_set_app_name (MateMixerContext *context, + const gchar *app_name); +gboolean mate_mixer_context_set_app_id (MateMixerContext *context, + const gchar *app_id); +gboolean mate_mixer_context_set_app_version (MateMixerContext *context, + const gchar *app_version); +gboolean mate_mixer_context_set_app_icon (MateMixerContext *context, + const gchar *app_icon); +gboolean mate_mixer_context_set_server_address (MateMixerContext *context, + const gchar *address); + +gboolean mate_mixer_context_open (MateMixerContext *context); +void mate_mixer_context_close (MateMixerContext *context); + +MateMixerState mate_mixer_context_get_state (MateMixerContext *context); + +MateMixerDevice * mate_mixer_context_get_device (MateMixerContext *context, + const gchar *name); +MateMixerStream * mate_mixer_context_get_stream (MateMixerContext *context, + const gchar *name); +MateMixerStream * mate_mixer_context_get_stored_stream (MateMixerContext *context, + const gchar *name); + +const GList * mate_mixer_context_list_devices (MateMixerContext *context); +const GList * mate_mixer_context_list_streams (MateMixerContext *context); +const GList * mate_mixer_context_list_stored_streams (MateMixerContext *context); + +MateMixerStream * mate_mixer_context_get_default_input_stream (MateMixerContext *context); +gboolean mate_mixer_context_set_default_input_stream (MateMixerContext *context, + MateMixerStream *stream); + +MateMixerStream * mate_mixer_context_get_default_output_stream (MateMixerContext *context); +gboolean mate_mixer_context_set_default_output_stream (MateMixerContext *context, + MateMixerStream *stream); + +const gchar * mate_mixer_context_get_backend_name (MateMixerContext *context); +MateMixerBackendType mate_mixer_context_get_backend_type (MateMixerContext *context); +MateMixerBackendFlags mate_mixer_context_get_backend_flags (MateMixerContext *context); + +G_END_DECLS + +#endif /* MATEMIXER_CONTEXT_H */ diff --git a/libmatemixer/matemixer-control.c b/libmatemixer/matemixer-control.c deleted file mode 100644 index b501f1e..0000000 --- a/libmatemixer/matemixer-control.c +++ /dev/null @@ -1,1466 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include -#include -#include - -#include "matemixer.h" -#include "matemixer-backend.h" -#include "matemixer-backend-module.h" -#include "matemixer-client-stream.h" -#include "matemixer-control.h" -#include "matemixer-enums.h" -#include "matemixer-enum-types.h" -#include "matemixer-private.h" -#include "matemixer-stream.h" - -/** - * SECTION:matemixer-control - * @short_description:The main class for interfacing with the library - * @include: libmatemixer/matemixer.h - */ - -struct _MateMixerControlPrivate -{ - GList *devices; - GList *streams; - GList *cached_streams; - gboolean backend_chosen; - MateMixerState state; - MateMixerBackend *backend; - MateMixerBackendData backend_data; - MateMixerBackendType backend_type; - MateMixerBackendModule *module; -}; - -enum { - PROP_0, - PROP_APP_NAME, - PROP_APP_ID, - PROP_APP_VERSION, - PROP_APP_ICON, - PROP_SERVER_ADDRESS, - PROP_STATE, - PROP_DEFAULT_INPUT, - PROP_DEFAULT_OUTPUT, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -enum { - DEVICE_ADDED, - DEVICE_REMOVED, - STREAM_ADDED, - STREAM_REMOVED, - CACHED_STREAM_ADDED, - CACHED_STREAM_REMOVED, - N_SIGNALS -}; - -static guint signals[N_SIGNALS] = { 0, }; - -static void mate_mixer_control_class_init (MateMixerControlClass *klass); - -static void mate_mixer_control_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void mate_mixer_control_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); - -static void mate_mixer_control_init (MateMixerControl *control); -static void mate_mixer_control_dispose (GObject *object); -static void mate_mixer_control_finalize (GObject *object); - -G_DEFINE_TYPE (MateMixerControl, mate_mixer_control, G_TYPE_OBJECT); - -static void on_backend_state_notify (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control); - -static void on_backend_device_added (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void on_backend_device_removed (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); - -static void on_backend_stream_added (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void on_backend_stream_removed (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); - -static void on_backend_cached_stream_added (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); -static void on_backend_cached_stream_removed (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control); - -static void on_backend_default_input_notify (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control); -static void on_backend_default_output_notify (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control); - -static gboolean try_next_backend (MateMixerControl *control); - -static void change_state (MateMixerControl *control, - MateMixerState state); - -static void close_control (MateMixerControl *control); - -static void free_backend (MateMixerControl *control); -static void free_devices (MateMixerControl *control); -static void free_streams (MateMixerControl *control); -static void free_cached_streams (MateMixerControl *control); - -static void -mate_mixer_control_class_init (MateMixerControlClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = mate_mixer_control_dispose; - object_class->finalize = mate_mixer_control_finalize; - object_class->get_property = mate_mixer_control_get_property; - object_class->set_property = mate_mixer_control_set_property; - - /** - * MateMixerControl:app-name: - * - * Localized human readable name of the application. - */ - properties[PROP_APP_NAME] = - g_param_spec_string ("app-name", - "App name", - "Application name", - NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - /** - * MateMixerControl:app-id: - * - * Identifier of the application (e.g. org.example.app). - */ - properties[PROP_APP_ID] = - g_param_spec_string ("app-id", - "App ID", - "Application identifier", - NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - /** - * MateMixerControl:app-version: - * - * Version of the application. - */ - properties[PROP_APP_VERSION] = - g_param_spec_string ("app-version", - "App version", - "Application version", - NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - /** - * MateMixerControl:app-icon: - * - * An XDG icon name for the application. - */ - properties[PROP_APP_ICON] = - g_param_spec_string ("app-icon", - "App icon", - "Application icon name", - NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - /** - * MateMixerControl:server-address: - * - * Address of the sound server to connect to. - * - * This feature is only supported in the PulseAudio backend. There is - * no need to specify an address in order to connect to the local daemon. - */ - properties[PROP_SERVER_ADDRESS] = - g_param_spec_string ("server-address", - "Server address", - "Sound server address", - NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_STATE] = - g_param_spec_enum ("state", - "State", - "Current backend connection state", - MATE_MIXER_TYPE_STATE, - MATE_MIXER_STATE_IDLE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - properties[PROP_DEFAULT_INPUT] = - g_param_spec_object ("default-input", - "Default input", - "Default input stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - properties[PROP_DEFAULT_OUTPUT] = - g_param_spec_object ("default-output", - "Default output", - "Default output stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - /** - * MateMixerControl::device-added: - * @control: a #MateMixerControl - * @name: name of the added device - * - * The signal is emitted each time a device is added to the system. - */ - signals[DEVICE_ADDED] = - g_signal_new ("device-added", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, device_added), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - /** - * MateMixerControl::device-removed: - * @control: a #MateMixerControl - * @name: name of the removed device - * - * The signal is emitted each time a device is removed from the system. - */ - signals[DEVICE_REMOVED] = - g_signal_new ("device-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, device_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - /** - * MateMixerControl::stream-added: - * @control: a #MateMixerControl - * @name: name of the added stream - * - * The signal is emitted each time a stream is created. - */ - signals[STREAM_ADDED] = - g_signal_new ("stream-added", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, stream_added), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - /** - * MateMixerControl::stream-removed: - * @control: a #MateMixerControl - * @name: name of the removed stream - * - * The signal is emitted each time a stream is removed. - */ - signals[STREAM_REMOVED] = - g_signal_new ("stream-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, stream_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - /** - * MateMixerControl::cached-stream-added: - * @control: a #MateMixerControl - * @name: name of the added cached stream - * - * The signal is emitted each time a cached stream is created. - */ - signals[CACHED_STREAM_ADDED] = - g_signal_new ("cached-stream-added", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, cached_stream_added), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - /** - * MateMixerControl::stream-removed: - * @control: a #MateMixerControl - * @name: name of the removed stream - * - * The signal is emitted each time a stream is removed. - */ - signals[CACHED_STREAM_REMOVED] = - g_signal_new ("cached-stream-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerControlClass, cached_stream_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - g_type_class_add_private (object_class, sizeof (MateMixerControlPrivate)); -} - -static void -mate_mixer_control_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - MateMixerControl *control; - - control = MATE_MIXER_CONTROL (object); - - switch (param_id) { - case PROP_APP_NAME: - g_value_set_string (value, control->priv->backend_data.app_name); - break; - case PROP_APP_ID: - g_value_set_string (value, control->priv->backend_data.app_id); - break; - case PROP_APP_VERSION: - g_value_set_string (value, control->priv->backend_data.app_version); - break; - case PROP_APP_ICON: - g_value_set_string (value, control->priv->backend_data.app_icon); - break; - case PROP_SERVER_ADDRESS: - g_value_set_string (value, control->priv->backend_data.server_address); - break; - case PROP_STATE: - g_value_set_enum (value, control->priv->state); - break; - case PROP_DEFAULT_INPUT: - g_value_set_object (value, mate_mixer_control_get_default_input_stream (control)); - break; - case PROP_DEFAULT_OUTPUT: - g_value_set_object (value, mate_mixer_control_get_default_output_stream (control)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -mate_mixer_control_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - MateMixerControl *control; - - control = MATE_MIXER_CONTROL (object); - - switch (param_id) { - case PROP_APP_NAME: - mate_mixer_control_set_app_name (control, g_value_get_string (value)); - break; - case PROP_APP_ID: - mate_mixer_control_set_app_id (control, g_value_get_string (value)); - break; - case PROP_APP_VERSION: - mate_mixer_control_set_app_version (control, g_value_get_string (value)); - break; - case PROP_APP_ICON: - mate_mixer_control_set_app_icon (control, g_value_get_string (value)); - break; - case PROP_SERVER_ADDRESS: - mate_mixer_control_set_server_address (control, g_value_get_string (value)); - break; - case PROP_DEFAULT_INPUT: - mate_mixer_control_set_default_input_stream (control, g_value_get_object (value)); - break; - case PROP_DEFAULT_OUTPUT: - mate_mixer_control_set_default_output_stream (control, g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -mate_mixer_control_init (MateMixerControl *control) -{ - control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, - MATE_MIXER_TYPE_CONTROL, - MateMixerControlPrivate); -} - -static void -mate_mixer_control_dispose (GObject *object) -{ - close_control (MATE_MIXER_CONTROL (object)); - - G_OBJECT_CLASS (mate_mixer_control_parent_class)->dispose (object); -} - -static void -mate_mixer_control_finalize (GObject *object) -{ - MateMixerControl *control; - - control = MATE_MIXER_CONTROL (object); - - g_free (control->priv->backend_data.app_name); - g_free (control->priv->backend_data.app_id); - g_free (control->priv->backend_data.app_version); - g_free (control->priv->backend_data.app_icon); - g_free (control->priv->backend_data.server_address); - - G_OBJECT_CLASS (mate_mixer_control_parent_class)->finalize (object); -} - -/** - * mate_mixer_control_new: - * - * Creates a new #MateMixerControl instance. - * - * Returns: a new #MateMixerControl instance or %NULL if the library has not - * been initialized using mate_mixer_init(). - */ -MateMixerControl * -mate_mixer_control_new (void) -{ - if (mate_mixer_is_initialized () == FALSE) { - g_critical ("The library has not been initialized"); - return NULL; - } - - return g_object_new (MATE_MIXER_TYPE_CONTROL, NULL); -} - -/** - * mate_mixer_control_set_backend_type: - * @control: a #MateMixerControl - * @backend_type: the sound system backend to use - * - * Makes the #MateMixerControl use the given #MateMixerBackendType. - * - * By default the backend type is determined automatically. This function can - * be used before mate_mixer_control_open() to alter this behavior and make the - * @control use the given backend. - * - * This function will fail if support for the backend is not installed in - * the system or if the current state is either %MATE_MIXER_STATE_CONNECTING or - * %MATE_MIXER_STATE_READY. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_control_set_backend_type (MateMixerControl *control, - MateMixerBackendType backend_type) -{ - MateMixerBackendModule *module; - const GList *modules; - const MateMixerBackendInfo *info; - - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - - if (control->priv->state == MATE_MIXER_STATE_CONNECTING || - control->priv->state == MATE_MIXER_STATE_READY) - return FALSE; - - modules = mate_mixer_get_modules (); - - while (modules != NULL) { - module = MATE_MIXER_BACKEND_MODULE (modules->data); - info = mate_mixer_backend_module_get_info (module); - - if (info->backend_type == backend_type) { - control->priv->backend_type = backend_type; - return TRUE; - } - modules = modules->next; - } - return FALSE; -} - -/** - * mate_mixer_control_set_app_name: - * @control: a #MateMixerControl - * @app_name: the name of your application, or %NULL to unset - * - * Sets the name of the application. This feature is only supported in the - * PulseAudio backend. - * - * This function will fail if the current state is either - * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should - * use it before opening a connection to the sound system. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_control_set_app_name (MateMixerControl *control, const gchar *app_name) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - - if (control->priv->state == MATE_MIXER_STATE_CONNECTING || - control->priv->state == MATE_MIXER_STATE_READY) - return FALSE; - - if (g_strcmp0 (control->priv->backend_data.app_name, app_name) != 0) { - g_free (control->priv->backend_data.app_name); - - control->priv->backend_data.app_name = g_strdup (app_name); - - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_NAME]); - } - return TRUE; -} - -/** - * mate_mixer_control_set_app_id: - * @control: a #MateMixerControl - * @app_id: the identifier of your application, or %NULL to unset - * - * Sets the identifier of the application (e.g. org.example.app). This feature - * is only supported in the PulseAudio backend. - * - * This function will fail if the current state is either - * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should - * use it before opening a connection to the sound system. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_control_set_app_id (MateMixerControl *control, const gchar *app_id) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - - if (control->priv->state == MATE_MIXER_STATE_CONNECTING || - control->priv->state == MATE_MIXER_STATE_READY) - return FALSE; - - if (g_strcmp0 (control->priv->backend_data.app_id, app_id) != 0) { - g_free (control->priv->backend_data.app_id); - - control->priv->backend_data.app_id = g_strdup (app_id); - - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_ID]); - } - return TRUE; -} - -/** - * mate_mixer_control_set_app_version: - * @control: a #MateMixerControl - * @app_version: the version of your application, or %NULL to unset - * - * Sets the version of the application. This feature is only supported in the - * PulseAudio backend. - * - * This function will fail if the current state is either - * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should - * use it before opening a connection to the sound system. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_control_set_app_version (MateMixerControl *control, const gchar *app_version) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - - if (control->priv->state == MATE_MIXER_STATE_CONNECTING || - control->priv->state == MATE_MIXER_STATE_READY) - return FALSE; - - if (g_strcmp0 (control->priv->backend_data.app_version, app_version) != 0) { - g_free (control->priv->backend_data.app_version); - - control->priv->backend_data.app_version = g_strdup (app_version); - - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_VERSION]); - } - return TRUE; -} - -/** - * mate_mixer_control_set_app_icon: - * @control: a #MateMixerControl - * @app_icon: the XDG icon name of your application, or %NULL to unset - * - * Sets the XDG icon name of the application. This feature is only supported in - * the PulseAudio backend. - * - * This function will fail if the current state is either - * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should - * use it before opening a connection to the sound system. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_control_set_app_icon (MateMixerControl *control, const gchar *app_icon) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - - if (control->priv->state == MATE_MIXER_STATE_CONNECTING || - control->priv->state == MATE_MIXER_STATE_READY) - return FALSE; - - if (g_strcmp0 (control->priv->backend_data.app_icon, app_icon) != 0) { - g_free (control->priv->backend_data.app_icon); - - control->priv->backend_data.app_icon = g_strdup (app_icon); - - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_APP_ICON]); - } - return TRUE; -} - -/** - * mate_mixer_control_set_server_address: - * @control: a #MateMixerControl - * @address: the address of the sound server to connect to or %NULL - * - * Sets the address of the sound server. This feature is only supported in the - * PulseAudio backend. If the address is not set, the default PulseAudio sound - * server will be used, which is normally the local daemon. - * - * This function will fail if the current state is either - * %MATE_MIXER_STATE_CONNECTING or %MATE_MIXER_STATE_READY, therefore you should - * use it before opening a connection to the sound system. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_control_set_server_address (MateMixerControl *control, const gchar *address) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - - if (control->priv->state == MATE_MIXER_STATE_CONNECTING || - control->priv->state == MATE_MIXER_STATE_READY) - return FALSE; - - if (g_strcmp0 (control->priv->backend_data.server_address, address) != 0) { - g_free (control->priv->backend_data.server_address); - - control->priv->backend_data.server_address = g_strdup (address); - - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_SERVER_ADDRESS]); - } - return TRUE; -} - -/** - * mate_mixer_control_open: - * @control: a #MateMixerControl - * - * Opens connection to a sound system. Unless the backend type was given - * beforehand, the library will find a working sound system automatically. - * If the automatic discovery fails to find a working sound system, it will - * use the "Null" backend, which provides no functionality. - * - * This function can complete the operation either synchronously or - * asynchronously. - * - * In case this function returns %TRUE, you should check the current state of - * the connection using mate_mixer_control_get_state(). If the current state - * is %MATE_MIXER_STATE_READY, the connection has been established successfully. - * Otherwise the state will be set to %MATE_MIXER_STATE_CONNECTING and the - * result of the operation will be determined asynchronously. You should wait - * for the state transition by connecting to the notification signal of the - * #MateMixerControl:state property. - * - * In case this function returns %FALSE, it is not possible to use the selected - * backend and the state will be set to %MATE_MIXER_STATE_FAILED. - * - * Returns: %TRUE on success or if the result will be determined asynchronously, - * or %FALSE on failure. - */ -gboolean -mate_mixer_control_open (MateMixerControl *control) -{ - MateMixerBackendModule *module = NULL; - MateMixerState state; - const GList *modules; - const MateMixerBackendInfo *info = NULL; - - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - - if (control->priv->state == MATE_MIXER_STATE_CONNECTING || - control->priv->state == MATE_MIXER_STATE_READY) - return FALSE; - - /* We are going to choose the first backend to try. It will be either the one - * specified by the application or the one with the highest priority */ - modules = mate_mixer_get_modules (); - - if (control->priv->backend_type != MATE_MIXER_BACKEND_UNKNOWN) { - while (modules != NULL) { - const MateMixerBackendInfo *info; - - module = MATE_MIXER_BACKEND_MODULE (modules->data); - info = mate_mixer_backend_module_get_info (module); - - if (info->backend_type == control->priv->backend_type) - break; - - module = NULL; - modules = modules->next; - } - } else { - /* The highest priority module is on the top of the list */ - module = MATE_MIXER_BACKEND_MODULE (modules->data); - } - if (module == NULL) { - /* Most likely the selected backend is not installed */ - change_state (control, MATE_MIXER_STATE_FAILED); - return FALSE; - } - - if (info == NULL) - info = mate_mixer_backend_module_get_info (module); - - control->priv->module = g_object_ref (module); - control->priv->backend = g_object_new (info->g_type, NULL); - - mate_mixer_backend_set_data (control->priv->backend, &control->priv->backend_data); - - /* This transitional state is always present, it will change to MATE_MIXER_STATE_READY - * or MATE_MIXER_STATE_FAILED either instantly or asynchronously */ - change_state (control, MATE_MIXER_STATE_CONNECTING); - - /* The backend initialization might fail in case it is known right now that - * the backend is unusable */ - if (mate_mixer_backend_open (control->priv->backend) == FALSE) { - if (control->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) { - /* User didn't request a specific backend, so try another one */ - return try_next_backend (control); - } - - /* User requested a specific backend and it failed */ - close_control (control); - change_state (control, MATE_MIXER_STATE_FAILED); - return FALSE; - } - - state = mate_mixer_backend_get_state (control->priv->backend); - - if (G_UNLIKELY (state != MATE_MIXER_STATE_READY && - state != MATE_MIXER_STATE_CONNECTING)) { - /* This would be a backend bug */ - g_warn_if_reached (); - - if (control->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) - return try_next_backend (control); - - close_control (control); - change_state (control, MATE_MIXER_STATE_FAILED); - return FALSE; - } - - g_signal_connect (G_OBJECT (control->priv->backend), - "notify::state", - G_CALLBACK (on_backend_state_notify), - control); - - change_state (control, state); - return TRUE; -} - -/** - * mate_mixer_control_close: - * @control: a #MateMixerControl - * - * Closes connection to the currently used sound system. The state will be - * set to %MATE_MIXER_STATE_IDLE. - */ -void -mate_mixer_control_close (MateMixerControl *control) -{ - g_return_if_fail (MATE_MIXER_IS_CONTROL (control)); - - close_control (control); - change_state (control, MATE_MIXER_STATE_IDLE); -} - -/** - * mate_mixer_control_get_state: - * @control: a #MateMixerControl - * - * Gets the current backend connection state of the #MateMixerControl. - * - * Returns: The current connection state. - */ -MateMixerState -mate_mixer_control_get_state (MateMixerControl *control) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), MATE_MIXER_STATE_UNKNOWN); - - return control->priv->state; -} - -/** - * mate_mixer_control_get_device: - * @control: a #MateMixerControl - * @name: a device name - * - * Gets the devices with the given name. - * - * Returns: a #MateMixerDevice or %NULL if there is no such device. - */ -MateMixerDevice * -mate_mixer_control_get_device (MateMixerControl *control, const gchar *name) -{ - const GList *list; - - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (name != NULL, NULL); - - list = mate_mixer_control_list_devices (control); - while (list != NULL) { - MateMixerDevice *device = MATE_MIXER_DEVICE (list->data); - - if (strcmp (name, mate_mixer_device_get_name (device)) == 0) - return device; - - list = list->next; - } - return NULL; -} - -/** - * mate_mixer_control_get_stream: - * @control: a #MateMixerControl - * @name: a stream name - * - * Gets the stream with the given name. - * - * Returns: a #MateMixerStream or %NULL if there is no such stream. - */ -MateMixerStream * -mate_mixer_control_get_stream (MateMixerControl *control, const gchar *name) -{ - const GList *list; - - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (name != NULL, NULL); - - list = mate_mixer_control_list_streams (control); - while (list != NULL) { - MateMixerStream *stream = MATE_MIXER_STREAM (list->data); - - if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) - return stream; - - list = list->next; - } - return NULL; -} - -/** - * mate_mixer_control_get_cached_stream: - * @control: a #MateMixerControl - * @name: a stream name - * - */ -MateMixerStream * -mate_mixer_control_get_cached_stream (MateMixerControl *control, const gchar *name) -{ - const GList *list; - - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - g_return_val_if_fail (name != NULL, NULL); - - list = mate_mixer_control_list_cached_streams (control); - while (list) { - MateMixerStream *stream = MATE_MIXER_STREAM (list->data); - - if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) - return stream; - - list = list->next; - } - return NULL; -} - -/** - * mate_mixer_control_list_devices: - * @control: a #MateMixerControl - * - * Gets a list of devices. Each list item is a #MateMixerDevice representing a - * hardware or software sound device in the system. - * - * The returned #GList is owned by the #MateMixerControl and may be invalidated - * at any time. - * - * Returns: a #GList of all devices in the system or %NULL if there are none or - * you are not connected to a sound system. - */ -const GList * -mate_mixer_control_list_devices (MateMixerControl *control) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - - if (control->priv->state != MATE_MIXER_STATE_READY) - return NULL; - - /* This list is cached here and invalidated when the backend notifies us - * about a change */ - if (control->priv->devices == NULL) - control->priv->devices = - mate_mixer_backend_list_devices (MATE_MIXER_BACKEND (control->priv->backend)); - - return (const GList *) control->priv->devices; -} - -/** - * mate_mixer_control_list_streams: - * @control: a #MateMixerControl - * - * Gets a list of streams. Each list item is a #MateMixerStream representing an - * input or output of a sound device. - * - * The returned #GList is owned by the #MateMixerControl and may be invalidated - * at any time. - * - * Returns: a #GList of all streams in the system or %NULL if there are none or - * you are not connected to a sound system. - */ -const GList * -mate_mixer_control_list_streams (MateMixerControl *control) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - - if (control->priv->state != MATE_MIXER_STATE_READY) - return NULL; - - /* This list is cached here and invalidated when the backend notifies us - * about a change */ - if (control->priv->streams == NULL) - control->priv->streams = - mate_mixer_backend_list_streams (MATE_MIXER_BACKEND (control->priv->backend)); - - return (const GList *) control->priv->streams; -} - -/** - * mate_mixer_control_list_cached_streams: - * @control: a #MateMixerControl - * - */ -const GList * -mate_mixer_control_list_cached_streams (MateMixerControl *control) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - - if (control->priv->state != MATE_MIXER_STATE_READY) - return NULL; - - /* This list is cached here and invalidated when the backend notifies us - * about a change */ - if (control->priv->cached_streams == NULL) - control->priv->cached_streams = - mate_mixer_backend_list_cached_streams (MATE_MIXER_BACKEND (control->priv->backend)); - - return (const GList *) control->priv->cached_streams; -} - -/** - * mate_mixer_control_get_default_input_stream: - * @control: a #MateMixerControl - * - * Gets the default input stream. The returned stream is where sound input is - * directed to by default. - * - * Returns: a #MateMixerStream or %NULL if there are no input streams in - * the system. - */ -MateMixerStream * -mate_mixer_control_get_default_input_stream (MateMixerControl *control) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - - if (control->priv->state != MATE_MIXER_STATE_READY) - return NULL; - - return mate_mixer_backend_get_default_input_stream (control->priv->backend); -} - -/** - * mate_mixer_control_set_default_input_stream: - * @control: a #MateMixerControl - * @stream: a #MateMixerStream to set as the default input stream - * - * Changes the default input stream in the system. The @stream must be an - * input non-client stream. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_control_set_default_input_stream (MateMixerControl *control, - MateMixerStream *stream) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - if (control->priv->state != MATE_MIXER_STATE_READY) - return FALSE; - - if (MATE_MIXER_IS_CLIENT_STREAM (stream)) { - g_warning ("Unable to set client stream as the default input stream"); - return FALSE; - } - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_INPUT)) { - g_warning ("Unable to set non-input stream as the default input stream"); - return FALSE; - } - - return mate_mixer_backend_set_default_input_stream (control->priv->backend, stream); -} - -/** - * mate_mixer_control_get_default_output_stream: - * @control: a #MateMixerControl - * - * Gets the default output stream. The returned stream is where sound output is - * directed to by default. - * - * Returns: a #MateMixerStream or %NULL if there are no output streams in - * the system. - */ -MateMixerStream * -mate_mixer_control_get_default_output_stream (MateMixerControl *control) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - - if (control->priv->state != MATE_MIXER_STATE_READY) - return NULL; - - return mate_mixer_backend_get_default_output_stream (control->priv->backend); -} - -/** - * mate_mixer_control_set_default_output_stream: - * @control: a #MateMixerControl - * @stream: a #MateMixerStream to set as the default output stream - * - * Changes the default output stream in the system. The @stream must be an - * output non-client stream. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_control_set_default_output_stream (MateMixerControl *control, - MateMixerStream *stream) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - if (control->priv->state != MATE_MIXER_STATE_READY) - return FALSE; - - if (MATE_MIXER_IS_CLIENT_STREAM (stream)) { - g_warning ("Unable to set client stream as the default output stream"); - return FALSE; - } - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_OUTPUT)) { - g_warning ("Unable to set non-output stream as the default output stream"); - return FALSE; - } - - return mate_mixer_backend_set_default_output_stream (control->priv->backend, stream); -} - -/** - * mate_mixer_control_get_backend_name: - * @control: a #MateMixerControl - * - * Gets the name of the currently used backend. This function will not - * work until connected to a sound system. - * - * Returns: the name or %NULL on error. - */ -const gchar * -mate_mixer_control_get_backend_name (MateMixerControl *control) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), NULL); - - if (control->priv->backend_chosen == FALSE) - return NULL; - - return mate_mixer_backend_module_get_info (control->priv->module)->name; -} - -/** - * mate_mixer_control_get_backend_type: - * @control: a #MateMixerControl - * - * Gets the type of the currently used backend. This function will not - * work until connected to a sound system. - * - * Returns: the backend type or %MATE_MIXER_BACKEND_UNKNOWN on error. - */ -MateMixerBackendType -mate_mixer_control_get_backend_type (MateMixerControl *control) -{ - g_return_val_if_fail (MATE_MIXER_IS_CONTROL (control), MATE_MIXER_BACKEND_UNKNOWN); - - if (control->priv->backend_chosen == FALSE) - return MATE_MIXER_BACKEND_UNKNOWN; - - return mate_mixer_backend_module_get_info (control->priv->module)->backend_type; -} - -static void -on_backend_state_notify (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control) -{ - MateMixerState state = mate_mixer_backend_get_state (backend); - - switch (state) { - case MATE_MIXER_STATE_CONNECTING: - g_debug ("Backend %s changed state to CONNECTING", - mate_mixer_backend_module_get_info (control->priv->module)->name); - - if (control->priv->backend_chosen == TRUE) { - /* Invalidate cached data when reconnecting */ - free_devices (control); - free_streams (control); - free_cached_streams (control); - } - change_state (control, state); - break; - - case MATE_MIXER_STATE_READY: - g_debug ("Backend %s changed state to READY", - mate_mixer_backend_module_get_info (control->priv->module)->name); - - change_state (control, state); - break; - - case MATE_MIXER_STATE_FAILED: - g_debug ("Backend %s changed state to FAILED", - mate_mixer_backend_module_get_info (control->priv->module)->name); - - if (control->priv->backend_type == MATE_MIXER_BACKEND_UNKNOWN) { - /* User didn't request a specific backend, so try another one */ - try_next_backend (control); - } else { - /* User requested a specific backend and it failed */ - close_control (control); - change_state (control, state); - } - break; - - default: - break; - } -} - -static void -on_backend_device_added (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) -{ - free_devices (control); - - g_signal_emit (G_OBJECT (control), - signals[DEVICE_ADDED], - 0, - name); -} - -static void -on_backend_device_removed (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) -{ - free_devices (control); - - g_signal_emit (G_OBJECT (control), - signals[DEVICE_REMOVED], - 0, - name); -} - -static void -on_backend_stream_added (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) -{ - free_streams (control); - - g_signal_emit (G_OBJECT (control), - signals[STREAM_ADDED], - 0, - name); -} - -static void -on_backend_stream_removed (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) -{ - free_streams (control); - - g_signal_emit (G_OBJECT (control), - signals[STREAM_REMOVED], - 0, - name); -} - -static void -on_backend_cached_stream_added (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) -{ - free_cached_streams (control); - - g_signal_emit (G_OBJECT (control), - signals[CACHED_STREAM_ADDED], - 0, - name); -} - -static void -on_backend_cached_stream_removed (MateMixerBackend *backend, - const gchar *name, - MateMixerControl *control) -{ - free_cached_streams (control); - - g_signal_emit (G_OBJECT (control), - signals[CACHED_STREAM_REMOVED], - 0, - name); -} - -static void -on_backend_default_input_notify (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control) -{ - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_DEFAULT_INPUT]); -} - -static void -on_backend_default_output_notify (MateMixerBackend *backend, - GParamSpec *pspec, - MateMixerControl *control) -{ - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_DEFAULT_OUTPUT]); -} - -static gboolean -try_next_backend (MateMixerControl *control) -{ - const GList *modules; - MateMixerBackendModule *module = NULL; - MateMixerState state; - - modules = mate_mixer_get_modules (); - - while (modules != NULL) { - if (control->priv->module == modules->data) { - /* Found the last tested backend, try to use the next one with a lower - * priority unless we have reached the end of the list */ - if (modules->next != NULL) - module = MATE_MIXER_BACKEND_MODULE (modules->next->data); - break; - } - modules = modules->next; - } - close_control (control); - - if (module == NULL) { - /* We have tried all the modules and all of them failed */ - change_state (control, MATE_MIXER_STATE_FAILED); - return FALSE; - } - - control->priv->module = g_object_ref (module); - control->priv->backend = - g_object_new (mate_mixer_backend_module_get_info (module)->g_type, NULL); - - mate_mixer_backend_set_data (control->priv->backend, &control->priv->backend_data); - - /* Try to open this backend and in case of failure keep trying until we find - * one that works or reach the end of the list */ - if (mate_mixer_backend_open (control->priv->backend) == FALSE) - return try_next_backend (control); - - state = mate_mixer_backend_get_state (control->priv->backend); - - if (G_UNLIKELY (state != MATE_MIXER_STATE_READY && - state != MATE_MIXER_STATE_CONNECTING)) { - /* This would be a backend bug */ - g_warn_if_reached (); - - return try_next_backend (control); - } - - g_signal_connect (G_OBJECT (control->priv->backend), - "notify::state", - G_CALLBACK (on_backend_state_notify), - control); - - change_state (control, state); - return TRUE; -} - -static void -change_state (MateMixerControl *control, MateMixerState state) -{ - if (control->priv->state == state) - return; - - control->priv->state = state; - - if (state == MATE_MIXER_STATE_READY && control->priv->backend_chosen == FALSE) { - /* It is safe to connect to the backend signals after reaching the READY - * state, because the app is not allowed to query any data before that state; - * therefore we won't end up in an inconsistent state by caching a list and - * then missing a notification about a change in the list */ - g_signal_connect (G_OBJECT (control->priv->backend), - "device-added", - G_CALLBACK (on_backend_device_added), - control); - g_signal_connect (G_OBJECT (control->priv->backend), - "device-removed", - G_CALLBACK (on_backend_device_removed), - control); - g_signal_connect (G_OBJECT (control->priv->backend), - "stream-added", - G_CALLBACK (on_backend_stream_added), - control); - g_signal_connect (G_OBJECT (control->priv->backend), - "stream-removed", - G_CALLBACK (on_backend_stream_removed), - control); - g_signal_connect (G_OBJECT (control->priv->backend), - "cached-stream-added", - G_CALLBACK (on_backend_cached_stream_added), - control); - g_signal_connect (G_OBJECT (control->priv->backend), - "cached-stream-removed", - G_CALLBACK (on_backend_cached_stream_removed), - control); - - g_signal_connect (G_OBJECT (control->priv->backend), - "notify::default-input", - G_CALLBACK (on_backend_default_input_notify), - control); - g_signal_connect (G_OBJECT (control->priv->backend), - "notify::default-output", - G_CALLBACK (on_backend_default_output_notify), - control); - - control->priv->backend_chosen = TRUE; - } - - g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_STATE]); -} - -static void -close_control (MateMixerControl *control) -{ - free_backend (control); - free_devices (control); - free_streams (control); - free_cached_streams (control); - - g_clear_object (&control->priv->module); - - control->priv->backend_chosen = FALSE; -} - -static void -free_backend (MateMixerControl *control) -{ - if (control->priv->backend == NULL) - return; - - g_signal_handlers_disconnect_by_data (G_OBJECT (control->priv->backend), - control); - - mate_mixer_backend_close (control->priv->backend); - - g_clear_object (&control->priv->backend); -} - -static void -free_devices (MateMixerControl *control) -{ - if (control->priv->devices == NULL) - return; - - g_list_free_full (control->priv->devices, g_object_unref); - - control->priv->devices = NULL; -} - -static void -free_streams (MateMixerControl *control) -{ - if (control->priv->streams == NULL) - return; - - g_list_free_full (control->priv->streams, g_object_unref); - - control->priv->streams = NULL; -} - -static void -free_cached_streams (MateMixerControl *control) -{ - if (control->priv->cached_streams == NULL) - return; - - g_list_free_full (control->priv->cached_streams, g_object_unref); - - control->priv->cached_streams = NULL; -} diff --git a/libmatemixer/matemixer-control.h b/libmatemixer/matemixer-control.h deleted file mode 100644 index e6d3afa..0000000 --- a/libmatemixer/matemixer-control.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#ifndef MATEMIXER_CONTROL_H -#define MATEMIXER_CONTROL_H - -#include -#include - -#include -#include -#include - -G_BEGIN_DECLS - -#define MATE_MIXER_TYPE_CONTROL \ - (mate_mixer_control_get_type ()) -#define MATE_MIXER_CONTROL(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_CONTROL, MateMixerControl)) -#define MATE_MIXER_IS_CONTROL(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_CONTROL)) -#define MATE_MIXER_CONTROL_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_CONTROL, MateMixerControlClass)) -#define MATE_MIXER_IS_CONTROL_CLASS(k) \ - (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_CONTROL)) -#define MATE_MIXER_CONTROL_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_CONTROL, MateMixerControlClass)) - -typedef struct _MateMixerControl MateMixerControl; -typedef struct _MateMixerControlClass MateMixerControlClass; -typedef struct _MateMixerControlPrivate MateMixerControlPrivate; - -/** - * MateMixerControl: - * - * The #MateMixerControl structure contains only private data and should only - * be accessed using the provided API. - */ -struct _MateMixerControl -{ - GObject parent; - - /*< private >*/ - MateMixerControlPrivate *priv; -}; - -/** - * MateMixerControlClass: - * - * The class structure of #MateMixerControl. - */ -struct _MateMixerControlClass -{ - GObjectClass parent_class; - - /*< private >*/ - /* Signals */ - void (*device_added) (MateMixerControl *control, - const gchar *name); - void (*device_changed) (MateMixerControl *control, - const gchar *name); - void (*device_removed) (MateMixerControl *control, - const gchar *name); - void (*stream_added) (MateMixerControl *control, - const gchar *name); - void (*stream_changed) (MateMixerControl *control, - const gchar *name); - void (*stream_removed) (MateMixerControl *control, - const gchar *name); - void (*cached_stream_added) (MateMixerControl *control, - const gchar *name); - void (*cached_stream_changed) (MateMixerControl *control, - const gchar *name); - void (*cached_stream_removed) (MateMixerControl *control, - const gchar *name); -}; - -GType mate_mixer_control_get_type (void) G_GNUC_CONST; - -MateMixerControl * mate_mixer_control_new (void); - -gboolean mate_mixer_control_set_backend_type (MateMixerControl *control, - MateMixerBackendType backend_type); -gboolean mate_mixer_control_set_app_name (MateMixerControl *control, - const gchar *app_name); -gboolean mate_mixer_control_set_app_id (MateMixerControl *control, - const gchar *app_id); -gboolean mate_mixer_control_set_app_version (MateMixerControl *control, - const gchar *app_version); -gboolean mate_mixer_control_set_app_icon (MateMixerControl *control, - const gchar *app_icon); -gboolean mate_mixer_control_set_server_address (MateMixerControl *control, - const gchar *address); - -gboolean mate_mixer_control_open (MateMixerControl *control); -void mate_mixer_control_close (MateMixerControl *control); - -MateMixerState mate_mixer_control_get_state (MateMixerControl *control); - -MateMixerDevice * mate_mixer_control_get_device (MateMixerControl *control, - const gchar *name); -MateMixerStream * mate_mixer_control_get_stream (MateMixerControl *control, - const gchar *name); -MateMixerStream * mate_mixer_control_get_cached_stream (MateMixerControl *control, - const gchar *name); - -const GList * mate_mixer_control_list_devices (MateMixerControl *control); -const GList * mate_mixer_control_list_streams (MateMixerControl *control); -const GList * mate_mixer_control_list_cached_streams (MateMixerControl *control); - -MateMixerStream * mate_mixer_control_get_default_input_stream (MateMixerControl *control); -gboolean mate_mixer_control_set_default_input_stream (MateMixerControl *control, - MateMixerStream *stream); - -MateMixerStream * mate_mixer_control_get_default_output_stream (MateMixerControl *control); -gboolean mate_mixer_control_set_default_output_stream (MateMixerControl *control, - MateMixerStream *stream); - -const gchar * mate_mixer_control_get_backend_name (MateMixerControl *control); -MateMixerBackendType mate_mixer_control_get_backend_type (MateMixerControl *control); - -G_END_DECLS - -#endif /* MATEMIXER_CONTROL_H */ diff --git a/libmatemixer/matemixer-device-profile-private.h b/libmatemixer/matemixer-device-profile-private.h index 403c7d7..44f2853 100644 --- a/libmatemixer/matemixer-device-profile-private.h +++ b/libmatemixer/matemixer-device-profile-private.h @@ -24,20 +24,20 @@ G_BEGIN_DECLS -MateMixerDeviceProfile *_mate_mixer_device_profile_new (const gchar *name, - const gchar *description, - guint priority, - guint input_streams, - guint output_streams); - -gboolean _mate_mixer_device_profile_update_description (MateMixerDeviceProfile *profile, - const gchar *description); -gboolean _mate_mixer_device_profile_update_priority (MateMixerDeviceProfile *profile, - guint priority); -gboolean _mate_mixer_device_profile_update_num_input_streams (MateMixerDeviceProfile *profile, - guint num); -gboolean _mate_mixer_device_profile_update_num_output_streams (MateMixerDeviceProfile *profile, - guint num); +MateMixerDeviceProfile *_mate_mixer_device_profile_new (const gchar *name, + const gchar *description, + guint priority, + guint input_streams, + guint output_streams); + +gboolean _mate_mixer_device_profile_set_label (MateMixerDeviceProfile *profile, + const gchar *label); +gboolean _mate_mixer_device_profile_set_priority (MateMixerDeviceProfile *profile, + guint priority); +gboolean _mate_mixer_device_profile_set_num_input_streams (MateMixerDeviceProfile *profile, + guint num); +gboolean _mate_mixer_device_profile_set_num_output_streams (MateMixerDeviceProfile *profile, + guint num); G_END_DECLS diff --git a/libmatemixer/matemixer-device-profile.c b/libmatemixer/matemixer-device-profile.c index 0485740..d841ff2 100644 --- a/libmatemixer/matemixer-device-profile.c +++ b/libmatemixer/matemixer-device-profile.c @@ -30,7 +30,7 @@ struct _MateMixerDeviceProfilePrivate { gchar *name; - gchar *description; + gchar *label; guint priority; guint num_input_streams; guint num_output_streams; @@ -39,7 +39,7 @@ struct _MateMixerDeviceProfilePrivate enum { PROP_0, PROP_NAME, - PROP_DESCRIPTION, + PROP_LABEL, PROP_PRIORITY, PROP_NUM_INPUT_STREAMS, PROP_NUM_OUTPUT_STREAMS, @@ -83,10 +83,10 @@ mate_mixer_device_profile_class_init (MateMixerDeviceProfileClass *klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - properties[PROP_DESCRIPTION] = - g_param_spec_string ("description", - "Description", - "Description of the profile", + properties[PROP_LABEL] = + g_param_spec_string ("label", + "Label", + "Label of the profile", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | @@ -144,18 +144,23 @@ mate_mixer_device_profile_get_property (GObject *object, case PROP_NAME: g_value_set_string (value, profile->priv->name); break; - case PROP_DESCRIPTION: - g_value_set_string (value, profile->priv->description); + + case PROP_LABEL: + g_value_set_string (value, profile->priv->label); break; + case PROP_PRIORITY: g_value_set_uint (value, profile->priv->priority); break; + case PROP_NUM_INPUT_STREAMS: g_value_set_uint (value, profile->priv->num_input_streams); break; + case PROP_NUM_OUTPUT_STREAMS: g_value_set_uint (value, profile->priv->num_output_streams); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -177,19 +182,24 @@ mate_mixer_device_profile_set_property (GObject *object, /* Construct-only string */ profile->priv->name = g_strdup (g_value_get_string (value)); break; - case PROP_DESCRIPTION: + + case PROP_LABEL: /* Construct-only string */ - profile->priv->description = g_strdup (g_value_get_string (value)); + profile->priv->label = g_strdup (g_value_get_string (value)); break; + case PROP_PRIORITY: profile->priv->priority = g_value_get_uint (value); break; + case PROP_NUM_INPUT_STREAMS: profile->priv->num_input_streams = g_value_get_uint (value); break; + case PROP_NUM_OUTPUT_STREAMS: profile->priv->num_output_streams = g_value_get_uint (value); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -212,7 +222,7 @@ mate_mixer_device_profile_finalize (GObject *object) profile = MATE_MIXER_DEVICE_PROFILE (object); g_free (profile->priv->name); - g_free (profile->priv->description); + g_free (profile->priv->label); G_OBJECT_CLASS (mate_mixer_device_profile_parent_class)->finalize (object); } @@ -230,15 +240,15 @@ mate_mixer_device_profile_get_name (MateMixerDeviceProfile *profile) } /** - * mate_mixer_device_profile_get_description: + * mate_mixer_device_profile_get_label: * @profile: a #MateMixerDeviceProfile */ const gchar * -mate_mixer_device_profile_get_description (MateMixerDeviceProfile *profile) +mate_mixer_device_profile_get_label (MateMixerDeviceProfile *profile) { g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), NULL); - return profile->priv->description; + return profile->priv->label; } /** @@ -279,14 +289,14 @@ mate_mixer_device_profile_get_num_output_streams (MateMixerDeviceProfile *profil MateMixerDeviceProfile * _mate_mixer_device_profile_new (const gchar *name, - const gchar *description, + const gchar *label, guint priority, guint input_streams, guint output_streams) { return g_object_new (MATE_MIXER_TYPE_DEVICE_PROFILE, "name", name, - "description", description, + "label", label, "priority", priority, "num-input-streams", input_streams, "num-output-streams", output_streams, @@ -294,17 +304,17 @@ _mate_mixer_device_profile_new (const gchar *name, } gboolean -_mate_mixer_device_profile_update_description (MateMixerDeviceProfile *profile, - const gchar *description) +_mate_mixer_device_profile_set_label (MateMixerDeviceProfile *profile, + const gchar *label) { g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); - if (g_strcmp0 (profile->priv->description, description) != 0) { - g_free (profile->priv->description); + if (g_strcmp0 (profile->priv->label, label) != 0) { + g_free (profile->priv->label); - profile->priv->description = g_strdup (description); + profile->priv->label = g_strdup (label); - g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_DESCRIPTION]); + g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_LABEL]); return TRUE; } @@ -312,8 +322,8 @@ _mate_mixer_device_profile_update_description (MateMixerDeviceProfile *profile, } gboolean -_mate_mixer_device_profile_update_priority (MateMixerDeviceProfile *profile, - guint priority) +_mate_mixer_device_profile_set_priority (MateMixerDeviceProfile *profile, + guint priority) { g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); @@ -328,8 +338,8 @@ _mate_mixer_device_profile_update_priority (MateMixerDeviceProfile *profile, } gboolean -_mate_mixer_device_profile_update_num_input_streams (MateMixerDeviceProfile *profile, - guint num) +_mate_mixer_device_profile_set_num_input_streams (MateMixerDeviceProfile *profile, + guint num) { g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); @@ -344,8 +354,8 @@ _mate_mixer_device_profile_update_num_input_streams (MateMixerDeviceProfile *pro } gboolean -_mate_mixer_device_profile_update_num_output_streams (MateMixerDeviceProfile *profile, - guint num) +_mate_mixer_device_profile_set_num_output_streams (MateMixerDeviceProfile *profile, + guint num) { g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); diff --git a/libmatemixer/matemixer-device-profile.h b/libmatemixer/matemixer-device-profile.h index e8fae19..8e4221a 100644 --- a/libmatemixer/matemixer-device-profile.h +++ b/libmatemixer/matemixer-device-profile.h @@ -20,6 +20,7 @@ #include #include +#include G_BEGIN_DECLS @@ -36,7 +37,6 @@ G_BEGIN_DECLS #define MATE_MIXER_DEVICE_PROFILE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_DEVICE_PROFILE, MateMixerDeviceProfileClass)) -typedef struct _MateMixerDeviceProfile MateMixerDeviceProfile; typedef struct _MateMixerDeviceProfileClass MateMixerDeviceProfileClass; typedef struct _MateMixerDeviceProfilePrivate MateMixerDeviceProfilePrivate; @@ -56,8 +56,9 @@ struct _MateMixerDeviceProfileClass GType mate_mixer_device_profile_get_type (void) G_GNUC_CONST; const gchar *mate_mixer_device_profile_get_name (MateMixerDeviceProfile *profile); -const gchar *mate_mixer_device_profile_get_description (MateMixerDeviceProfile *profile); +const gchar *mate_mixer_device_profile_get_label (MateMixerDeviceProfile *profile); guint mate_mixer_device_profile_get_priority (MateMixerDeviceProfile *profile); + guint mate_mixer_device_profile_get_num_input_streams (MateMixerDeviceProfile *profile); guint mate_mixer_device_profile_get_num_output_streams (MateMixerDeviceProfile *profile); diff --git a/libmatemixer/matemixer-device.c b/libmatemixer/matemixer-device.c index 87517d6..229110f 100644 --- a/libmatemixer/matemixer-device.c +++ b/libmatemixer/matemixer-device.c @@ -15,12 +15,14 @@ * License along with this library; if not, see . */ +#include #include #include #include "matemixer-device.h" #include "matemixer-device-profile.h" -#include "matemixer-port.h" +#include "matemixer-stream.h" +#include "matemixer-switch.h" /** * SECTION:matemixer-device @@ -28,45 +30,270 @@ * @include: libmatemixer/matemixer.h */ -G_DEFINE_INTERFACE (MateMixerDevice, mate_mixer_device, G_TYPE_OBJECT) +struct _MateMixerDevicePrivate +{ + gchar *name; + gchar *label; + gchar *icon; + GList *streams; + GList *switches; + GList *profiles; + MateMixerDeviceProfile *profile; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_LABEL, + PROP_ICON, + PROP_ACTIVE_PROFILE, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +enum { + STREAM_ADDED, + STREAM_REMOVED, + SWITCH_ADDED, + SWITCH_REMOVED, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0, }; + +static void mate_mixer_device_class_init (MateMixerDeviceClass *klass); + +static void mate_mixer_device_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_device_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_device_init (MateMixerDevice *device); +static void mate_mixer_device_dispose (GObject *object); +static void mate_mixer_device_finalize (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (MateMixerDevice, mate_mixer_device, G_TYPE_OBJECT) + +static MateMixerStream * mate_mixer_device_real_get_stream (MateMixerDevice *device, + const gchar *name); +static MateMixerSwitch * mate_mixer_device_real_get_switch (MateMixerDevice *device, + const gchar *name); +static MateMixerDeviceProfile *mate_mixer_device_real_get_profile (MateMixerDevice *device, + const gchar *name); + +static void +mate_mixer_device_class_init (MateMixerDeviceClass *klass) +{ + GObjectClass *object_class; + + klass->get_stream = mate_mixer_device_real_get_stream; + klass->get_switch = mate_mixer_device_real_get_switch; + klass->get_profile = mate_mixer_device_real_get_profile; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = mate_mixer_device_dispose; + object_class->finalize = mate_mixer_device_finalize; + object_class->get_property = mate_mixer_device_get_property; + object_class->set_property = mate_mixer_device_set_property; + + properties[PROP_NAME] = + g_param_spec_string ("name", + "Name", + "Name of the device", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_LABEL] = + g_param_spec_string ("label", + "Label", + "Label of the device", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ICON] = + g_param_spec_string ("icon", + "Icon", + "Name of the sound device icon", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ACTIVE_PROFILE] = + g_param_spec_object ("active-profile", + "Active profile", + "The currently active profile of the device", + MATE_MIXER_TYPE_DEVICE_PROFILE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + signals[STREAM_ADDED] = + g_signal_new ("stream-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerDeviceClass, stream_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[STREAM_REMOVED] = + g_signal_new ("stream-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerDeviceClass, stream_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[SWITCH_ADDED] = + g_signal_new ("switch-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerDeviceClass, switch_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[SWITCH_REMOVED] = + g_signal_new ("switch-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (MateMixerDeviceClass, switch_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + g_type_class_add_private (object_class, sizeof (MateMixerDevicePrivate)); +} + +static void +mate_mixer_device_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerDevice *device; + + device = MATE_MIXER_DEVICE (object); + + switch (param_id) { + case PROP_NAME: + g_value_set_string (value, device->priv->name); + break; + case PROP_LABEL: + g_value_set_string (value, device->priv->label); + break; + case PROP_ICON: + g_value_set_string (value, device->priv->icon); + break; + case PROP_ACTIVE_PROFILE: + g_value_set_object (value, device->priv->profile); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_device_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerDevice *device; + + device = MATE_MIXER_DEVICE (object); + + switch (param_id) { + case PROP_NAME: + /* Construct-only string */ + device->priv->name = g_strdup (g_value_get_string (value)); + break; + case PROP_LABEL: + /* Construct-only string */ + device->priv->label = 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; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} static void -mate_mixer_device_default_init (MateMixerDeviceInterface *iface) -{ - g_object_interface_install_property (iface, - g_param_spec_string ("name", - "Name", - "Name of the device", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_string ("description", - "Description", - "Description of the device", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_string ("icon", - "Icon", - "Name of the sound device icon", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_object ("active-profile", - "Active profile", - "The currently active profile of the device", - MATE_MIXER_TYPE_DEVICE_PROFILE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); +mate_mixer_device_init (MateMixerDevice *device) +{ + device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, + MATE_MIXER_TYPE_DEVICE, + MateMixerDevicePrivate); +} + +static void +mate_mixer_device_dispose (GObject *object) +{ + MateMixerDevice *device; + + device = MATE_MIXER_DEVICE (object); + + if (device->priv->streams != NULL) { + g_list_free_full (device->priv->streams, g_object_unref); + device->priv->streams = NULL; + } + if (device->priv->switches != NULL) { + g_list_free_full (device->priv->switches, g_object_unref); + device->priv->switches = NULL; + } + if (device->priv->profiles != NULL) { + g_list_free_full (device->priv->profiles, g_object_unref); + device->priv->profiles = NULL; + } + + g_clear_object (&device->priv->profile); + + G_OBJECT_CLASS (mate_mixer_device_parent_class)->dispose (object); +} + +static void +mate_mixer_device_finalize (GObject *object) +{ + MateMixerDevice *device; + + device = MATE_MIXER_DEVICE (object); + + g_free (device->priv->name); + g_free (device->priv->label); + g_free (device->priv->icon); + + G_OBJECT_CLASS (mate_mixer_device_parent_class)->finalize (object); } /** @@ -78,8 +305,7 @@ mate_mixer_device_get_name (MateMixerDevice *device) { g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - /* Implementation required */ - return MATE_MIXER_DEVICE_GET_INTERFACE (device)->get_name (device); + return device->priv->name; } /** @@ -87,18 +313,11 @@ mate_mixer_device_get_name (MateMixerDevice *device) * @device: a #MateMixerDevice */ const gchar * -mate_mixer_device_get_description (MateMixerDevice *device) +mate_mixer_device_get_label (MateMixerDevice *device) { - MateMixerDeviceInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); - - if (iface->get_description) - return iface->get_description (device); - - return NULL; + return device->priv->label; } /** @@ -108,77 +327,80 @@ mate_mixer_device_get_description (MateMixerDevice *device) const gchar * mate_mixer_device_get_icon (MateMixerDevice *device) { - MateMixerDeviceInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); - - if (iface->get_icon) - return iface->get_icon (device); - - return NULL; + return device->priv->icon; } /** - * mate_mixer_device_get_port: + * mate_mixer_device_get_profile: * @device: a #MateMixerDevice - * @name: a port name + * @name: a profile name */ -MateMixerPort * -mate_mixer_device_get_port (MateMixerDevice *device, const gchar *name) +MateMixerDeviceProfile * +mate_mixer_device_get_profile (MateMixerDevice *device, const gchar *name) { - MateMixerDeviceInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - g_return_val_if_fail (name != NULL, NULL); - - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); - - if (iface->get_port) - return iface->get_port (device, name); + return MATE_MIXER_DEVICE_GET_CLASS (device)->get_profile (device, name); +} - return NULL; +/** + * mate_mixer_device_get_stream: + * @device: a #MateMixerDevice + * @name: a profile name + */ +MateMixerStream * +mate_mixer_device_get_stream (MateMixerDevice *device, const gchar *name) +{ + return MATE_MIXER_DEVICE_GET_CLASS (device)->get_stream (device, name); } /** - * mate_mixer_device_get_profile: + * mate_mixer_device_get_switch: * @device: a #MateMixerDevice * @name: a profile name */ -MateMixerDeviceProfile * -mate_mixer_device_get_profile (MateMixerDevice *device, const gchar *name) +MateMixerSwitch * +mate_mixer_device_get_switch (MateMixerDevice *device, const gchar *name) { - MateMixerDeviceInterface *iface; + return MATE_MIXER_DEVICE_GET_CLASS (device)->get_switch (device, name); +} +/** + * mate_mixer_device_list_streams: + * @device: a #MateMixerDevice + */ +const GList * +mate_mixer_device_list_streams (MateMixerDevice *device) +{ g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - g_return_val_if_fail (name != NULL, NULL); - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); + if (device->priv->streams == NULL) { + MateMixerDeviceClass *klass = MATE_MIXER_DEVICE_GET_CLASS (device); - if (iface->get_profile) - return iface->get_profile (device, name); + if (klass->list_streams != NULL) + device->priv->streams = klass->list_streams (device); + } - return NULL; + return (const GList *) device->priv->streams; } /** - * mate_mixer_device_list_ports: + * mate_mixer_device_list_switches: * @device: a #MateMixerDevice */ const GList * -mate_mixer_device_list_ports (MateMixerDevice *device) +mate_mixer_device_list_switches (MateMixerDevice *device) { - MateMixerDeviceInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); + if (device->priv->switches == NULL) { + MateMixerDeviceClass *klass = MATE_MIXER_DEVICE_GET_CLASS (device); - if (iface->list_ports) - return iface->list_ports (device); + if (klass->list_switches != NULL) + device->priv->switches = klass->list_switches (device); + } - return NULL; + return (const GList *) device->priv->switches; } /** @@ -188,16 +410,16 @@ mate_mixer_device_list_ports (MateMixerDevice *device) const GList * mate_mixer_device_list_profiles (MateMixerDevice *device) { - MateMixerDeviceInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); + if (device->priv->profiles == NULL) { + MateMixerDeviceClass *klass = MATE_MIXER_DEVICE_GET_CLASS (device); - if (iface->list_profiles) - return iface->list_profiles (device); + if (klass->list_profiles != NULL) + device->priv->profiles = klass->list_profiles (device); + } - return NULL; + return (const GList *) device->priv->profiles; } /** @@ -207,16 +429,9 @@ mate_mixer_device_list_profiles (MateMixerDevice *device) MateMixerDeviceProfile * mate_mixer_device_get_active_profile (MateMixerDevice *device) { - MateMixerDeviceInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); - - if (iface->get_active_profile) - return iface->get_active_profile (device); - - return NULL; + return device->priv->profile; } /** @@ -228,15 +443,83 @@ gboolean mate_mixer_device_set_active_profile (MateMixerDevice *device, MateMixerDeviceProfile *profile) { - MateMixerDeviceInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), FALSE); g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); - iface = MATE_MIXER_DEVICE_GET_INTERFACE (device); + if (profile != device->priv->profile) { + MateMixerDeviceClass *klass; + + klass = MATE_MIXER_DEVICE_GET_CLASS (device); + + if (klass->set_active_profile == NULL || + klass->set_active_profile (device, profile) == FALSE) + return FALSE; - if (iface->set_active_profile) - return iface->set_active_profile (device, profile); + if (G_LIKELY (device->priv->profile != NULL)) + g_object_unref (device->priv->profile); - return FALSE; + device->priv->profile = g_object_ref (profile); + } + + return TRUE; +} + +static MateMixerStream * +mate_mixer_device_real_get_stream (MateMixerDevice *device, const gchar *name) +{ + const GList *list; + + g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); + g_return_val_if_fail (name != NULL, NULL); + + list = mate_mixer_device_list_streams (device); + while (list != NULL) { + MateMixerStream *stream = MATE_MIXER_STREAM (list->data); + + if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) + return stream; + + list = list->next; + } + return NULL; +} + +static MateMixerSwitch * +mate_mixer_device_real_get_switch (MateMixerDevice *device, const gchar *name) +{ + const GList *list; + + g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); + g_return_val_if_fail (name != NULL, NULL); + + list = mate_mixer_device_list_switches (device); + while (list != NULL) { + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (list->data); + + if (strcmp (name, mate_mixer_switch_get_name (swtch)) == 0) + return swtch; + + list = list->next; + } + return NULL; +} + +static MateMixerDeviceProfile * +mate_mixer_device_real_get_profile (MateMixerDevice *device, const gchar *name) +{ + const GList *list; + + g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); + g_return_val_if_fail (name != NULL, NULL); + + list = mate_mixer_device_list_profiles (device); + while (list != NULL) { + MateMixerDeviceProfile *profile = MATE_MIXER_DEVICE_PROFILE (list->data); + + if (strcmp (name, mate_mixer_device_profile_get_name (profile)) == 0) + return profile; + + list = list->next; + } + return NULL; } diff --git a/libmatemixer/matemixer-device.h b/libmatemixer/matemixer-device.h index 340496b..885460c 100644 --- a/libmatemixer/matemixer-device.h +++ b/libmatemixer/matemixer-device.h @@ -21,8 +21,7 @@ #include #include -#include -#include +#include "matemixer-types.h" G_BEGIN_DECLS @@ -32,48 +31,72 @@ G_BEGIN_DECLS (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_DEVICE, MateMixerDevice)) #define MATE_MIXER_IS_DEVICE(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_DEVICE)) -#define MATE_MIXER_DEVICE_GET_INTERFACE(o) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_DEVICE, MateMixerDeviceInterface)) +#define MATE_MIXER_DEVICE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_DEVICE, MateMixerDeviceClass)) +#define MATE_MIXER_IS_DEVICE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_DEVICE)) +#define MATE_MIXER_DEVICE_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_DEVICE, MateMixerDeviceClass)) -typedef struct _MateMixerDevice MateMixerDevice; /* dummy object */ -typedef struct _MateMixerDeviceInterface MateMixerDeviceInterface; +typedef struct _MateMixerDeviceClass MateMixerDeviceClass; +typedef struct _MateMixerDevicePrivate MateMixerDevicePrivate; -struct _MateMixerDeviceInterface +struct _MateMixerDevice { - GTypeInterface parent_iface; + GObject object; + + /*< private >*/ + MateMixerDevicePrivate *priv; +}; + +struct _MateMixerDeviceClass +{ + GObjectClass parent_class; /*< private >*/ - /* Virtual table */ - const gchar *(*get_name) (MateMixerDevice *device); - const gchar *(*get_description) (MateMixerDevice *device); - const gchar *(*get_icon) (MateMixerDevice *device); - MateMixerPort *(*get_port) (MateMixerDevice *device, + MateMixerStream *(*get_stream) (MateMixerDevice *device, + const gchar *name); + MateMixerSwitch *(*get_switch) (MateMixerDevice *device, const gchar *name); MateMixerDeviceProfile *(*get_profile) (MateMixerDevice *device, const gchar *name); - const GList *(*list_streams) (MateMixerDevice *device); - const GList *(*list_ports) (MateMixerDevice *device); - const GList *(*list_profiles) (MateMixerDevice *device); + GList *(*list_streams) (MateMixerDevice *device); + GList *(*list_switches) (MateMixerDevice *device); + GList *(*list_profiles) (MateMixerDevice *device); - MateMixerDeviceProfile *(*get_active_profile) (MateMixerDevice *device); gboolean (*set_active_profile) (MateMixerDevice *device, MateMixerDeviceProfile *profile); + + /* Signals */ + void (*stream_added) (MateMixerDevice *device, + const gchar *name); + void (*stream_removed) (MateMixerDevice *device, + const gchar *name); + void (*switch_added) (MateMixerDevice *device, + const gchar *name); + void (*switch_removed) (MateMixerDevice *device, + const gchar *name); }; GType mate_mixer_device_get_type (void) G_GNUC_CONST; const gchar * mate_mixer_device_get_name (MateMixerDevice *device); -const gchar * mate_mixer_device_get_description (MateMixerDevice *device); +const gchar * mate_mixer_device_get_label (MateMixerDevice *device); const gchar * mate_mixer_device_get_icon (MateMixerDevice *device); -MateMixerPort * mate_mixer_device_get_port (MateMixerDevice *device, +MateMixerStream * mate_mixer_device_get_stream (MateMixerDevice *device, const gchar *name); + +MateMixerSwitch * mate_mixer_device_get_switch (MateMixerDevice *device, + const gchar *name); + MateMixerDeviceProfile *mate_mixer_device_get_profile (MateMixerDevice *device, const gchar *name); -const GList * mate_mixer_device_list_ports (MateMixerDevice *device); +const GList * mate_mixer_device_list_streams (MateMixerDevice *device); +const GList * mate_mixer_device_list_switches (MateMixerDevice *device); const GList * mate_mixer_device_list_profiles (MateMixerDevice *device); MateMixerDeviceProfile *mate_mixer_device_get_active_profile (MateMixerDevice *device); diff --git a/libmatemixer/matemixer-enum-types.c b/libmatemixer/matemixer-enum-types.c index 50e3bf4..035be3d 100644 --- a/libmatemixer/matemixer-enum-types.c +++ b/libmatemixer/matemixer-enum-types.c @@ -135,7 +135,7 @@ mate_mixer_stream_control_flags_get_type (void) { MATE_MIXER_STREAM_CONTROL_NO_FLAGS, "MATE_MIXER_STREAM_CONTROL_NO_FLAGS", "no-flags" }, { MATE_MIXER_STREAM_CONTROL_HAS_MUTE, "MATE_MIXER_STREAM_CONTROL_HAS_MUTE", "has-mute" }, { MATE_MIXER_STREAM_CONTROL_HAS_VOLUME, "MATE_MIXER_STREAM_CONTROL_HAS_VOLUME", "has-volume" }, - { MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL_VOLUME, "MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL_VOLUME", "has-decibel-volume" }, + { MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL, "MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL", "has-decibel" }, { MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME, "MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME", "has-flat-volume" }, { MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME, "MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME", "can-set-volume" }, { MATE_MIXER_STREAM_CONTROL_CAN_BALANCE, "MATE_MIXER_STREAM_CONTROL_CAN_BALANCE", "can-balance" }, @@ -149,6 +149,30 @@ mate_mixer_stream_control_flags_get_type (void) return etype; } +GType +mate_mixer_stream_control_role_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + { MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, "MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN", "unknown" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, "MATE_MIXER_STREAM_CONTROL_ROLE_MASTER", "master" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_PORT, "MATE_MIXER_STREAM_CONTROL_ROLE_PORT", "port" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_PCM, "MATE_MIXER_STREAM_CONTROL_ROLE_PCM", "pcm" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_BASS, "MATE_MIXER_STREAM_CONTROL_ROLE_BASS", "bass" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, "MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE", "treble" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_CD, "MATE_MIXER_STREAM_CONTROL_ROLE_CD", "cd" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, "MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER", "speaker" }, + { 0, NULL, NULL } + }; + etype = g_enum_register_static ( + g_intern_static_string ("MateMixerStreamControlRole"), + values); + } + return etype; +} + GType mate_mixer_client_stream_flags_get_type (void) { diff --git a/libmatemixer/matemixer-enum-types.h b/libmatemixer/matemixer-enum-types.h index 1dc13cc..f2193be 100644 --- a/libmatemixer/matemixer-enum-types.h +++ b/libmatemixer/matemixer-enum-types.h @@ -46,6 +46,9 @@ GType mate_mixer_stream_state_get_type (void) G_GNUC_CONST; #define MATE_MIXER_TYPE_STREAM_CONTROL_FLAGS (mate_mixer_stream_control_flags_get_type ()) GType mate_mixer_stream_control_flags_get_type (void) G_GNUC_CONST; +#define MATE_MIXER_TYPE_STREAM_CONTROL_ROLE (mate_mixer_stream_control_role_get_type ()) +GType mate_mixer_stream_control_role_get_type (void); + #define MATE_MIXER_TYPE_CLIENT_STREAM_FLAGS (mate_mixer_client_stream_flags_get_type ()) GType mate_mixer_client_stream_flags_get_type (void) G_GNUC_CONST; diff --git a/libmatemixer/matemixer-enums.h b/libmatemixer/matemixer-enums.h index b99f4c5..8a88b1c 100644 --- a/libmatemixer/matemixer-enums.h +++ b/libmatemixer/matemixer-enums.h @@ -47,8 +47,8 @@ typedef enum { * PulseAudio sound system backend. It has the highest priority and * will be the first one to try unless you select a specific backend * to connect to. + * @MATE_MIXER_BACKEND_ALSA: * @MATE_MIXER_BACKEND_OSS: - * @MATE_MIXER_BACKEND_OSS4: * @MATE_MIXER_BACKEND_NULL: * Fallback backend which never fails to initialize, but provides no * functionality. This backend has the lowest priority and will be used @@ -58,11 +58,17 @@ typedef enum { typedef enum { MATE_MIXER_BACKEND_UNKNOWN, MATE_MIXER_BACKEND_PULSEAUDIO, + MATE_MIXER_BACKEND_ALSA, MATE_MIXER_BACKEND_OSS, - MATE_MIXER_BACKEND_OSS4, MATE_MIXER_BACKEND_NULL } MateMixerBackendType; +typedef enum { /*< flags >*/ + MATE_MIXER_BACKEND_NO_FLAGS = 0, + MATE_MIXER_BACKEND_CAN_SET_DEFAULT_INPUT_STREAM, + MATE_MIXER_BACKEND_CAN_SET_DEFAULT_OUTPUT_STREAM +} MateMixerBackendFlags; + /** * MateMixerPortFlags: * @MATE_MIXER_PORT_NO_FLAGS: @@ -116,7 +122,7 @@ typedef enum { * @MATE_MIXER_STREAM_CONTROL_NO_FLAGS: * @MATE_MIXER_STREAM_CONTROL_HAS_MUTE: * @MATE_MIXER_STREAM_CONTROL_HAS_VOLUME: - * @MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL_VOLUME: + * @MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL: * @MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME: * @MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME: * @MATE_MIXER_STREAM_CONTROL_CAN_BALANCE: @@ -126,22 +132,23 @@ typedef enum { MATE_MIXER_STREAM_CONTROL_NO_FLAGS = 0, MATE_MIXER_STREAM_CONTROL_HAS_MUTE = 1 << 0, MATE_MIXER_STREAM_CONTROL_HAS_VOLUME = 1 << 1, - MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL_VOLUME = 1 << 2, + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL = 1 << 2, MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME = 1 << 3, - MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME = 1 << 4, - MATE_MIXER_STREAM_CONTROL_CAN_BALANCE = 1 << 5, - MATE_MIXER_STREAM_CONTROL_CAN_FADE = 1 << 6 + MATE_MIXER_STREAM_CONTROL_CAN_SET_MUTE = 1 << 4, + MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME = 1 << 5, + MATE_MIXER_STREAM_CONTROL_CAN_BALANCE = 1 << 6, + MATE_MIXER_STREAM_CONTROL_CAN_FADE = 1 << 7 } MateMixerStreamControlFlags; typedef enum { MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, + MATE_MIXER_STREAM_CONTROL_ROLE_PORT, MATE_MIXER_STREAM_CONTROL_ROLE_PCM, MATE_MIXER_STREAM_CONTROL_ROLE_BASS, MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, MATE_MIXER_STREAM_CONTROL_ROLE_CD, MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, - MATE_MIXER_STREAM_CONTROL_ROLE_PORT } MateMixerStreamControlRole; /** @@ -210,7 +217,7 @@ typedef enum { * @MATE_MIXER_CHANNEL_TOP_BACK_CENTER: */ typedef enum { - MATE_MIXER_CHANNEL_UNKNOWN, + MATE_MIXER_CHANNEL_UNKNOWN = 0, MATE_MIXER_CHANNEL_MONO, MATE_MIXER_CHANNEL_FRONT_LEFT, MATE_MIXER_CHANNEL_FRONT_RIGHT, @@ -229,7 +236,8 @@ typedef enum { MATE_MIXER_CHANNEL_TOP_CENTER, MATE_MIXER_CHANNEL_TOP_BACK_LEFT, MATE_MIXER_CHANNEL_TOP_BACK_RIGHT, - MATE_MIXER_CHANNEL_TOP_BACK_CENTER + MATE_MIXER_CHANNEL_TOP_BACK_CENTER, + MATE_MIXER_CHANNEL_MAX } MateMixerChannelPosition; #endif /* MATEMIXER_ENUMS_H */ diff --git a/libmatemixer/matemixer-port-private.h b/libmatemixer/matemixer-port-private.h deleted file mode 100644 index a696d27..0000000 --- a/libmatemixer/matemixer-port-private.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#ifndef MATEMIXER_PORT_PRIVATE_H -#define MATEMIXER_PORT_PRIVATE_H - -#include - -#include "matemixer-enums.h" -#include "matemixer-port.h" - -G_BEGIN_DECLS - -MateMixerPort *_mate_mixer_port_new (const gchar *name, - const gchar *description, - const gchar *icon, - guint priority, - MateMixerPortFlags flags); - -gboolean _mate_mixer_port_update_description (MateMixerPort *port, - const gchar *description); -gboolean _mate_mixer_port_update_icon (MateMixerPort *port, - const gchar *icon); -gboolean _mate_mixer_port_update_priority (MateMixerPort *port, - guint priority); -gboolean _mate_mixer_port_update_flags (MateMixerPort *port, - MateMixerPortFlags flags); - -G_END_DECLS - -#endif /* MATEMIXER_PORT_PRIVATE_H */ diff --git a/libmatemixer/matemixer-port.c b/libmatemixer/matemixer-port.c deleted file mode 100644 index f89a8bb..0000000 --- a/libmatemixer/matemixer-port.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include -#include - -#include "matemixer-enums.h" -#include "matemixer-enum-types.h" -#include "matemixer-port.h" -#include "matemixer-port-private.h" - -/** - * SECTION:matemixer-port - * @include: libmatemixer/matemixer.h - */ - -struct _MateMixerPortPrivate -{ - gchar *name; - gchar *description; - gchar *icon; - guint priority; - MateMixerPortFlags flags; -}; - -enum { - PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_ICON, - PROP_PRIORITY, - PROP_FLAGS, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -static void mate_mixer_port_class_init (MateMixerPortClass *klass); - -static void mate_mixer_port_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void mate_mixer_port_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); - -static void mate_mixer_port_init (MateMixerPort *port); -static void mate_mixer_port_finalize (GObject *object); - -G_DEFINE_TYPE (MateMixerPort, mate_mixer_port, G_TYPE_OBJECT); - -static void -mate_mixer_port_class_init (MateMixerPortClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = mate_mixer_port_finalize; - object_class->get_property = mate_mixer_port_get_property; - object_class->set_property = mate_mixer_port_set_property; - - properties[PROP_NAME] = - g_param_spec_string ("name", - "Name", - "Name of the port", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_DESCRIPTION] = - g_param_spec_string ("description", - "Description", - "Description of the port", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_ICON] = - g_param_spec_string ("icon", - "Icon", - "Name of the port icon", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_PRIORITY] = - g_param_spec_uint ("priority", - "Priority", - "Priority of the port", - 0, - G_MAXUINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_FLAGS] = - g_param_spec_flags ("flags", - "Flags", - "Capability flags of the port", - MATE_MIXER_TYPE_PORT_FLAGS, - MATE_MIXER_PORT_NO_FLAGS, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (object_class, sizeof (MateMixerPortPrivate)); -} - -static void -mate_mixer_port_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - MateMixerPort *port; - - port = MATE_MIXER_PORT (object); - - switch (param_id) { - case PROP_NAME: - g_value_set_string (value, port->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, port->priv->description); - break; - case PROP_ICON: - g_value_set_string (value, port->priv->icon); - break; - case PROP_PRIORITY: - g_value_set_uint (value, port->priv->priority); - break; - case PROP_FLAGS: - g_value_set_flags (value, port->priv->flags); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -mate_mixer_port_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - MateMixerPort *port; - - port = MATE_MIXER_PORT (object); - - switch (param_id) { - case PROP_NAME: - /* Construct-only string */ - port->priv->name = g_strdup (g_value_get_string (value)); - break; - case PROP_DESCRIPTION: - /* Construct-only string */ - port->priv->description = g_strdup (g_value_get_string (value)); - break; - case PROP_ICON: - /* Construct-only string */ - port->priv->icon = g_strdup (g_value_get_string (value)); - break; - case PROP_PRIORITY: - port->priv->priority = g_value_get_uint (value); - break; - case PROP_FLAGS: - port->priv->flags = g_value_get_flags (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -mate_mixer_port_init (MateMixerPort *port) -{ - port->priv = G_TYPE_INSTANCE_GET_PRIVATE (port, - MATE_MIXER_TYPE_PORT, - MateMixerPortPrivate); -} - -static void -mate_mixer_port_finalize (GObject *object) -{ - MateMixerPort *port; - - port = MATE_MIXER_PORT (object); - - g_free (port->priv->name); - g_free (port->priv->description); - g_free (port->priv->icon); - - G_OBJECT_CLASS (mate_mixer_port_parent_class)->finalize (object); -} - -/** - * mate_mixer_port_get_name: - * @port: a #MateMixerPort - */ -const gchar * -mate_mixer_port_get_name (MateMixerPort *port) -{ - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), NULL); - - return port->priv->name; -} - -/** - * mate_mixer_port_get_description: - * @port: a #MateMixerPort - */ -const gchar * -mate_mixer_port_get_description (MateMixerPort *port) -{ - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), NULL); - - return port->priv->description; -} - -/** - * mate_mixer_port_get_icon: - * @port: a #MateMixerPort - */ -const gchar * -mate_mixer_port_get_icon (MateMixerPort *port) -{ - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), NULL); - - return port->priv->icon; -} - -/** - * mate_mixer_port_get_priority: - * @port: a #MateMixerPort - */ -guint -mate_mixer_port_get_priority (MateMixerPort *port) -{ - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), 0); - - return port->priv->priority; -} - -/** - * mate_mixer_port_get_flags: - * @port: a #MateMixerPort - */ -MateMixerPortFlags -mate_mixer_port_get_flags (MateMixerPort *port) -{ - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), MATE_MIXER_PORT_NO_FLAGS); - - return port->priv->flags; -} - -MateMixerPort * -_mate_mixer_port_new (const gchar *name, - const gchar *description, - const gchar *icon, - guint priority, - MateMixerPortFlags flags) -{ - return g_object_new (MATE_MIXER_TYPE_PORT, - "name", name, - "description", description, - "icon", icon, - "priority", priority, - "flags", flags, - NULL); -} - -gboolean -_mate_mixer_port_update_description (MateMixerPort *port, const gchar *description) -{ - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - - if (g_strcmp0 (port->priv->description, description) != 0) { - g_free (port->priv->description); - - port->priv->description = g_strdup (description); - - g_object_notify_by_pspec (G_OBJECT (port), properties[PROP_DESCRIPTION]); - return TRUE; - } - - return FALSE; -} - -gboolean -_mate_mixer_port_update_icon (MateMixerPort *port, const gchar *icon) -{ - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - - if (g_strcmp0 (port->priv->icon, icon) != 0) { - g_free (port->priv->icon); - - port->priv->icon = g_strdup (icon); - - g_object_notify_by_pspec (G_OBJECT (port), properties[PROP_ICON]); - return TRUE; - } - - return FALSE; -} - -gboolean -_mate_mixer_port_update_priority (MateMixerPort *port, guint priority) -{ - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - - if (port->priv->priority != priority) { - port->priv->priority = priority; - - g_object_notify_by_pspec (G_OBJECT (port), properties[PROP_PRIORITY]); - return TRUE; - } - - return FALSE; -} - -gboolean -_mate_mixer_port_update_flags (MateMixerPort *port, MateMixerPortFlags flags) -{ - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - - if (port->priv->flags != flags) { - port->priv->flags = flags; - - g_object_notify_by_pspec (G_OBJECT (port), properties[PROP_FLAGS]); - return TRUE; - } - - return FALSE; -} diff --git a/libmatemixer/matemixer-port.h b/libmatemixer/matemixer-port.h deleted file mode 100644 index 61f16f5..0000000 --- a/libmatemixer/matemixer-port.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#ifndef MATEMIXER_PORT_H -#define MATEMIXER_PORT_H - -#include -#include - -#include - -G_BEGIN_DECLS - -#define MATE_MIXER_TYPE_PORT \ - (mate_mixer_port_get_type ()) -#define MATE_MIXER_PORT(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_PORT, MateMixerPort)) -#define MATE_MIXER_IS_PORT(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_PORT)) -#define MATE_MIXER_PORT_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_PORT, MateMixerPortClass)) -#define MATE_MIXER_IS_PORT_CLASS(k) \ - (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_PORT)) -#define MATE_MIXER_PORT_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_PORT, MateMixerPortClass)) - -typedef struct _MateMixerPort MateMixerPort; -typedef struct _MateMixerPortClass MateMixerPortClass; -typedef struct _MateMixerPortPrivate MateMixerPortPrivate; - -struct _MateMixerPort -{ - GObject parent; - - /*< private >*/ - MateMixerPortPrivate *priv; -}; - -struct _MateMixerPortClass -{ - GObjectClass parent_class; -}; - -GType mate_mixer_port_get_type (void) G_GNUC_CONST; - -const gchar * mate_mixer_port_get_name (MateMixerPort *port); -const gchar * mate_mixer_port_get_description (MateMixerPort *port); -const gchar * mate_mixer_port_get_icon (MateMixerPort *port); -guint mate_mixer_port_get_priority (MateMixerPort *port); -MateMixerPortFlags mate_mixer_port_get_flags (MateMixerPort *port); - -G_END_DECLS - -#endif /* MATEMIXER_PORT_H */ diff --git a/libmatemixer/matemixer-private.h b/libmatemixer/matemixer-private.h index 4dde496..4229000 100644 --- a/libmatemixer/matemixer-private.h +++ b/libmatemixer/matemixer-private.h @@ -20,9 +20,91 @@ #include +#include "matemixer-backend-module.h" +#include "matemixer-backend-private.h" +#include "matemixer-device-profile-private.h" +#include "matemixer-stream-control-private.h" +#include "matemixer-switch-option-private.h" +#include "matemixer-switch-private.h" + G_BEGIN_DECLS -const GList *mate_mixer_get_modules (void); +#define MATE_MIXER_IS_LEFT_CHANNEL(c) \ + ((c) == MATE_MIXER_CHANNEL_FRONT_LEFT || \ + (c) == MATE_MIXER_CHANNEL_BACK_LEFT || \ + (c) == MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER || \ + (c) == MATE_MIXER_CHANNEL_SIDE_LEFT || \ + (c) == MATE_MIXER_CHANNEL_TOP_FRONT_LEFT || \ + (c) == MATE_MIXER_CHANNEL_TOP_BACK_LEFT) + +#define MATE_MIXER_IS_RIGHT_CHANNEL(c) \ + ((c) == MATE_MIXER_CHANNEL_FRONT_RIGHT || \ + (c) == MATE_MIXER_CHANNEL_BACK_RIGHT || \ + (c) == MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER || \ + (c) == MATE_MIXER_CHANNEL_SIDE_RIGHT || \ + (c) == MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT || \ + (c) == MATE_MIXER_CHANNEL_TOP_BACK_RIGHT) + +#define MATE_MIXER_IS_FRONT_CHANNEL(c) \ + ((c) == MATE_MIXER_CHANNEL_FRONT_LEFT || \ + (c) == MATE_MIXER_CHANNEL_FRONT_RIGHT || \ + (c) == MATE_MIXER_CHANNEL_FRONT_CENTER || \ + (c) == MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER || \ + (c) == MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER || \ + (c) == MATE_MIXER_CHANNEL_TOP_FRONT_LEFT || \ + (c) == MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT || \ + (c) == MATE_MIXER_CHANNEL_TOP_FRONT_CENTER) + +#define MATE_MIXER_IS_BACK_CHANNEL(c) \ + ((c) == MATE_MIXER_CHANNEL_BACK_LEFT || \ + (c) == MATE_MIXER_CHANNEL_BACK_RIGHT || \ + (c) == MATE_MIXER_CHANNEL_BACK_CENTER || \ + (c) == MATE_MIXER_CHANNEL_TOP_BACK_LEFT || \ + (c) == MATE_MIXER_CHANNEL_TOP_BACK_RIGHT || \ + (c) == MATE_MIXER_CHANNEL_TOP_BACK_CENTER) + +#define MATE_MIXER_CHANNEL_MASK_LEFT \ + ((1 << MATE_MIXER_CHANNEL_FRONT_LEFT) | \ + (1 << MATE_MIXER_CHANNEL_BACK_LEFT) | \ + (1 << MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER) | \ + (1 << MATE_MIXER_CHANNEL_SIDE_LEFT) | \ + (1 << MATE_MIXER_CHANNEL_TOP_FRONT_LEFT) | \ + (1 << MATE_MIXER_CHANNEL_TOP_BACK_LEFT)) + +#define MATE_MIXER_CHANNEL_MASK_RIGHT \ + ((1 << MATE_MIXER_CHANNEL_FRONT_RIGHT) | \ + (1 << MATE_MIXER_CHANNEL_BACK_RIGHT) | \ + (1 << MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER) | \ + (1 << MATE_MIXER_CHANNEL_SIDE_RIGHT) | \ + (1 << MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT) | \ + (1 << MATE_MIXER_CHANNEL_TOP_BACK_RIGHT)) + +#define MATE_MIXER_CHANNEL_MASK_FRONT \ + ((1 << MATE_MIXER_CHANNEL_FRONT_LEFT) | \ + (1 << MATE_MIXER_CHANNEL_FRONT_RIGHT) | \ + (1 << MATE_MIXER_CHANNEL_FRONT_CENTER) | \ + (1 << MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER) | \ + (1 << MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER) | \ + (1 << MATE_MIXER_CHANNEL_TOP_FRONT_LEFT) | \ + (1 << MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT) | \ + (1 << MATE_MIXER_CHANNEL_TOP_FRONT_CENTER)) + +#define MATE_MIXER_CHANNEL_MASK_BACK \ + ((1 << MATE_MIXER_CHANNEL_BACK_LEFT) | \ + (1 << MATE_MIXER_CHANNEL_BACK_RIGHT) | \ + (1 << MATE_MIXER_CHANNEL_BACK_CENTER) | \ + (1 << MATE_MIXER_CHANNEL_TOP_BACK_LEFT) | \ + (1 << MATE_MIXER_CHANNEL_TOP_BACK_RIGHT) | \ + (1 << MATE_MIXER_CHANNEL_TOP_BACK_CENTER)) + +#define MATE_MIXER_CHANNEL_MASK_HAS_CHANNEL(m,c) ((m) & (1 << (c))) +#define MATE_MIXER_CHANNEL_MASK_HAS_LEFT(m) ((m) & MATE_MIXER_CHANNEL_MASK_LEFT) +#define MATE_MIXER_CHANNEL_MASK_HAS_RIGHT(m) ((m) & MATE_MIXER_CHANNEL_MASK_RIGHT) +#define MATE_MIXER_CHANNEL_MASK_HAS_FRONT(m) ((m) & MATE_MIXER_CHANNEL_MASK_FRONT) +#define MATE_MIXER_CHANNEL_MASK_HAS_BACK(m) ((m) & MATE_MIXER_CHANNEL_MASK_BACK) + +const GList *_mate_mixer_get_modules (void); +guint32 _mate_mixer_create_channel_mask (MateMixerChannelPosition *positions, guint n); G_END_DECLS diff --git a/libmatemixer/matemixer-stream-control-private.h b/libmatemixer/matemixer-stream-control-private.h new file mode 100644 index 0000000..49454b3 --- /dev/null +++ b/libmatemixer/matemixer-stream-control-private.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_STREAM_CONTROL_PRIVATE_H +#define MATEMIXER_STREAM_CONTROL_PRIVATE_H + +#include +#include "matemixer-enums.h" +#include "matemixer-types.h" + +G_BEGIN_DECLS + +void _mate_mixer_stream_control_set_flags (MateMixerStreamControl *control, + MateMixerStreamControlFlags flags); + +void _mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, + gboolean mute); + +void _mate_mixer_stream_control_set_balance (MateMixerStreamControl *control, + gfloat balance); + +void _mate_mixer_stream_control_set_fade (MateMixerStreamControl *control, + gfloat fade); + +G_END_DECLS + +#endif /* MATEMIXER_STREAM_CONTROL_PRIVATE_H */ diff --git a/libmatemixer/matemixer-stream-control.c b/libmatemixer/matemixer-stream-control.c index 388308e..ace584a 100644 --- a/libmatemixer/matemixer-stream-control.c +++ b/libmatemixer/matemixer-stream-control.c @@ -21,425 +21,607 @@ #include "matemixer-enums.h" #include "matemixer-enum-types.h" #include "matemixer-stream-control.h" +#include "matemixer-stream-control-private.h" /** * SECTION:matemixer-stream-control * @include: libmatemixer/matemixer.h */ -G_DEFINE_INTERFACE (MateMixerStreamControl, mate_mixer_stream_control, G_TYPE_OBJECT) +struct _MateMixerStreamControlPrivate +{ + gchar *name; + gchar *label; + gboolean mute; + gfloat balance; + gfloat fade; + MateMixerStreamControlFlags flags; + MateMixerStreamControlRole role; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_LABEL, + PROP_FLAGS, + PROP_ROLE, + PROP_MUTE, + PROP_VOLUME, + PROP_BALANCE, + PROP_FADE, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void mate_mixer_stream_control_class_init (MateMixerStreamControlClass *klass); + +static void mate_mixer_stream_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_stream_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_stream_control_init (MateMixerStreamControl *control); +static void mate_mixer_stream_control_finalize (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (MateMixerStreamControl, mate_mixer_stream_control, G_TYPE_OBJECT) + +static void +mate_mixer_stream_control_class_init (MateMixerStreamControlClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = mate_mixer_stream_control_finalize; + object_class->get_property = mate_mixer_stream_control_get_property; + object_class->set_property = mate_mixer_stream_control_set_property; + + properties[PROP_NAME] = + g_param_spec_string ("name", + "Name", + "Name of the stream control", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_LABEL] = + g_param_spec_string ("label", + "Label", + "Label of the stream control", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_FLAGS] = + g_param_spec_flags ("flags", + "Flags", + "Capability flags of the stream control", + MATE_MIXER_TYPE_STREAM_CONTROL_FLAGS, + MATE_MIXER_STREAM_CONTROL_NO_FLAGS, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ROLE] = + g_param_spec_enum ("role", + "Role", + "Role of the stream control", + MATE_MIXER_TYPE_STREAM_CONTROL_ROLE, + MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_MUTE] = + g_param_spec_boolean ("mute", + "Mute", + "Mute state of the stream control", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_VOLUME] = + g_param_spec_uint ("volume", + "Volume", + "Volume of the stream control", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_BALANCE] = + g_param_spec_float ("balance", + "Balance", + "Balance value of the stream control", + -1.0f, + 1.0f, + 0.0f, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_FADE] = + g_param_spec_float ("fade", + "Fade", + "Fade value of the stream control", + -1.0f, + 1.0f, + 0.0f, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (object_class, sizeof (MateMixerStreamControlPrivate)); +} + +static void +mate_mixer_stream_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerStreamControl *control; + + control = MATE_MIXER_STREAM_CONTROL (object); + + switch (param_id) { + case PROP_NAME: + g_value_set_string (value, control->priv->name); + break; + case PROP_LABEL: + g_value_set_string (value, control->priv->label); + break; + case PROP_FLAGS: + g_value_set_flags (value, control->priv->flags); + break; + case PROP_ROLE: + g_value_set_enum (value, control->priv->role); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_stream_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerStreamControl *control; + + control = MATE_MIXER_STREAM_CONTROL (object); + + switch (param_id) { + case PROP_NAME: + /* Construct-only string */ + control->priv->name = g_value_dup_string (value); + break; + case PROP_LABEL: + /* Construct-only string */ + control->priv->label = g_value_dup_string (value); + break; + case PROP_FLAGS: + control->priv->flags = g_value_get_flags (value); + break; + case PROP_ROLE: + control->priv->role = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_stream_control_init (MateMixerStreamControl *control) +{ + control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, + MATE_MIXER_TYPE_STREAM_CONTROL, + MateMixerStreamControlPrivate); +} static void -mate_mixer_stream_control_default_init (MateMixerStreamControlInterface *iface) -{ - g_object_interface_install_property (iface, - g_param_spec_string ("name", - "Name", - "Name of the stream control", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_string ("description", - "Description", - "Description of the stream control", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_flags ("flags", - "Flags", - "Capability flags of the stream control", - MATE_MIXER_TYPE_STREAM_CONTROL_FLAGS, - MATE_MIXER_STREAM_CONTROL_NO_FLAGS, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_boolean ("mute", - "Mute", - "Mute state of the stream control", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_uint ("volume", - "Volume", - "Volume of the stream control", - 0, - G_MAXUINT, - 0, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_float ("balance", - "Balance", - "Balance value of the stream control", - -1.0f, - 1.0f, - 0.0f, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_float ("fade", - "Fade", - "Fade value of the stream control", - -1.0f, - 1.0f, - 0.0f, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); +mate_mixer_stream_control_finalize (GObject *object) +{ + MateMixerStreamControl *control; + + control = MATE_MIXER_STREAM_CONTROL (object); + + g_free (control->priv->name); + g_free (control->priv->label); + + G_OBJECT_CLASS (mate_mixer_stream_control_parent_class)->finalize (object); } const gchar * -mate_mixer_stream_control_get_name (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_name (MateMixerStreamControl *control) { - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), NULL); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), NULL); - /* Implementation required */ - return MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl)->get_name (ctl); + return control->priv->name; } const gchar * -mate_mixer_stream_control_get_description (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_label (MateMixerStreamControl *control) { - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), NULL); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), NULL); - /* Implementation required */ - return MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl)->get_description (ctl); + return control->priv->label; } MateMixerStreamControlFlags -mate_mixer_stream_control_get_flags (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_flags (MateMixerStreamControl *control) { - MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), MATE_MIXER_STREAM_CONTROL_NO_FLAGS); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), MATE_MIXER_STREAM_CONTROL_NO_FLAGS); + return control->priv->flags; +} - g_object_get (G_OBJECT (ctl), - "flags", &flags, - NULL); +MateMixerStreamControlRole +mate_mixer_stream_control_get_role (MateMixerStreamControl *control) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN); - return flags; + return control->priv->role; } gboolean -mate_mixer_stream_control_get_mute (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_mute (MateMixerStreamControl *control) { - gboolean mute = FALSE; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - g_object_get (G_OBJECT (ctl), - "mute", &mute, - NULL); - - return mute; + return control->priv->mute; } gboolean -mate_mixer_stream_control_set_mute (MateMixerStreamControl *ctl, gboolean mute) +mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, gboolean mute) { - MateMixerStreamControlInterface *iface; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + if (control->priv->mute == mute) + return TRUE; - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_MUTE) { + MateMixerStreamControlClass *klass; - if (iface->set_mute != NULL) - return iface->set_mute (ctl, mute); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (G_LIKELY (klass->set_mute != NULL)) + return klass->set_mute (control, mute); + } return FALSE; } guint -mate_mixer_stream_control_get_volume (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *control) { - guint volume = 0; + MateMixerStreamControlClass *klass; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - g_object_get (G_OBJECT (ctl), - "volume", &volume, - NULL); + if (klass->get_num_channels != NULL) + return klass->get_num_channels (control); - return volume; + return 0; } -gboolean -mate_mixer_stream_control_set_volume (MateMixerStreamControl *ctl, guint volume) +guint +mate_mixer_stream_control_get_volume (MateMixerStreamControl *control) { - MateMixerStreamControlInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_VOLUME) { + MateMixerStreamControlClass *klass; - if (iface->set_volume != NULL) - return iface->set_volume (ctl, volume); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - return FALSE; + if G_LIKELY (klass->get_volume != NULL) + return klass->get_volume (control); + } + return 0; } -gdouble -mate_mixer_stream_control_get_decibel (MateMixerStreamControl *ctl) +gboolean +mate_mixer_stream_control_set_volume (MateMixerStreamControl *control, guint volume) { - MateMixerStreamControlInterface *iface; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), -MATE_MIXER_INFINITY); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_VOLUME) { + MateMixerStreamControlClass *klass; - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (iface->get_decibel != NULL) - return iface->get_decibel (ctl); - - return -MATE_MIXER_INFINITY; + if (G_LIKELY (klass->set_volume != NULL)) + return klass->set_volume (control, volume); + } + return FALSE; } -gboolean -mate_mixer_stream_control_set_decibel (MateMixerStreamControl *ctl, gdouble decibel) +gdouble +mate_mixer_stream_control_get_decibel (MateMixerStreamControl *control) { - MateMixerStreamControlInterface *iface; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), -MATE_MIXER_INFINITY); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) { + MateMixerStreamControlClass *klass; - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (iface->set_decibel != NULL) - return iface->set_decibel (ctl, decibel); - - return FALSE; + if (G_LIKELY (klass->get_decibel != NULL)) + return klass->get_decibel (control); + } + return -MATE_MIXER_INFINITY; } -guint -mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *ctl) +gboolean +mate_mixer_stream_control_set_decibel (MateMixerStreamControl *control, gdouble decibel) { - MateMixerStreamControlInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) { + MateMixerStreamControlClass *klass; - if (iface->get_num_channels != NULL) - return iface->get_num_channels (ctl); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - return 0; + if (G_LIKELY (klass->set_decibel != NULL)) + return klass->set_decibel (control, decibel); + } + return FALSE; } MateMixerChannelPosition -mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *ctl, guint channel) +mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *control, guint channel) { - MateMixerStreamControlInterface *iface; + MateMixerStreamControlClass *klass; - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), MATE_MIXER_CHANNEL_UNKNOWN); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), MATE_MIXER_CHANNEL_UNKNOWN); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (iface->get_channel_position != NULL) - return iface->get_channel_position (ctl, channel); + if (klass->get_channel_position != NULL) + return klass->get_channel_position (control, channel); return MATE_MIXER_CHANNEL_UNKNOWN; } guint -mate_mixer_stream_control_get_channel_volume (MateMixerStreamControl *ctl, guint channel) +mate_mixer_stream_control_get_channel_volume (MateMixerStreamControl *control, guint channel) { - MateMixerStreamControlInterface *iface; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_VOLUME) { + MateMixerStreamControlClass *klass; - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); - - if (iface->get_channel_volume != NULL) - return iface->get_channel_volume (ctl, channel); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (G_LIKELY (klass->get_channel_volume != NULL)) + return klass->get_channel_volume (control, channel); + } return 0; } gboolean -mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *ctl, +mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *control, guint channel, guint volume) { - MateMixerStreamControlInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_VOLUME) { + MateMixerStreamControlClass *klass; - if (iface->set_channel_volume != NULL) - return iface->set_channel_volume (ctl, channel, volume); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (G_LIKELY (klass->set_channel_volume != NULL)) + return klass->set_channel_volume (control, channel, volume); + } return FALSE; } gdouble -mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *ctl, guint channel) +mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *control, guint channel) { - MateMixerStreamControlInterface *iface; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), -MATE_MIXER_INFINITY); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), -MATE_MIXER_INFINITY); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) { + MateMixerStreamControlClass *klass; - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); - - if (iface->get_channel_decibel != NULL) - return iface->get_channel_decibel (ctl, channel); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (G_LIKELY (klass->get_channel_decibel != NULL)) + return klass->get_channel_decibel (control, channel); + } return -MATE_MIXER_INFINITY; } gboolean -mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *ctl, +mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *control, guint channel, gdouble decibel) { - MateMixerStreamControlInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) { + MateMixerStreamControlClass *klass; - if (iface->set_channel_decibel != NULL) - return iface->set_channel_decibel (ctl, channel, decibel); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (G_LIKELY (klass->set_channel_decibel != NULL)) + return klass->set_channel_decibel (control, channel, decibel); + } return FALSE; } gboolean -mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *ctl, +mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *control, MateMixerChannelPosition position) { - MateMixerStreamControlInterface *iface; + MateMixerStreamControlClass *klass; - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (iface->has_channel_position != NULL) - return iface->has_channel_position (ctl, position); + if (klass->has_channel_position != NULL) + return klass->has_channel_position (control, position); return FALSE; } gfloat -mate_mixer_stream_control_get_balance (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_balance (MateMixerStreamControl *control) { - gfloat balance = 0.0f; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0.0f); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0.0f); - - g_object_get (G_OBJECT (ctl), - "balance", &balance, - NULL); - - return balance; + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_CAN_BALANCE) + return control->priv->balance; + else + return 0.0f; } gboolean -mate_mixer_stream_control_set_balance (MateMixerStreamControl *ctl, gfloat balance) +mate_mixer_stream_control_set_balance (MateMixerStreamControl *control, gfloat balance) { - MateMixerStreamControlInterface *iface; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_CAN_BALANCE) { + MateMixerStreamControlClass *klass; - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + if (balance < -1.0f || balance > 1.0f) + return FALSE; - if (iface->set_balance != NULL) - return iface->set_balance (ctl, balance); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (G_LIKELY (klass->set_balance != NULL)) + return klass->set_balance (control, balance); + } return FALSE; } gfloat -mate_mixer_stream_control_get_fade (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_fade (MateMixerStreamControl *control) { - gfloat fade = 0.0f; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0.0f); - - g_object_get (G_OBJECT (ctl), - "fade", &fade, - NULL); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0.0f); - return fade; + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_CAN_FADE) + return control->priv->fade; + else + return 0.0f; } gboolean -mate_mixer_stream_control_set_fade (MateMixerStreamControl *ctl, gfloat fade) +mate_mixer_stream_control_set_fade (MateMixerStreamControl *control, gfloat fade) { - MateMixerStreamControlInterface *iface; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), FALSE); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_CAN_FADE) { + MateMixerStreamControlClass *klass; - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + if (fade < -1.0f || fade > 1.0f) + return FALSE; - if (iface->set_fade != NULL) - return iface->set_fade (ctl, fade); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (G_LIKELY (klass->set_fade != NULL)) + return klass->set_fade (control, fade); + } return FALSE; } guint -mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *control) { - MateMixerStreamControlInterface *iface; + MateMixerStreamControlClass *klass; - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (iface->get_min_volume != NULL) - return iface->get_min_volume (ctl); + if (klass->get_min_volume != NULL) + return klass->get_min_volume (control); return 0; } guint -mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *control) { - MateMixerStreamControlInterface *iface; + MateMixerStreamControlClass *klass; - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (iface->get_max_volume != NULL) - return iface->get_max_volume (ctl); + if (klass->get_max_volume != NULL) + return klass->get_max_volume (control); return 0; } guint -mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *control) { - MateMixerStreamControlInterface *iface; + MateMixerStreamControlClass *klass; - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (iface->get_normal_volume != NULL) - return iface->get_normal_volume (ctl); + if (klass->get_normal_volume != NULL) + return klass->get_normal_volume (control); return 0; } guint -mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *ctl) +mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *control) { - MateMixerStreamControlInterface *iface; + MateMixerStreamControlClass *klass; - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (ctl), 0); + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); - iface = MATE_MIXER_STREAM_CONTROL_GET_INTERFACE (ctl); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (iface->get_base_volume != NULL) - return iface->get_base_volume (ctl); + if (klass->get_base_volume != NULL) + return klass->get_base_volume (control); return 0; } + +void +_mate_mixer_stream_control_set_flags (MateMixerStreamControl *control, + MateMixerStreamControlFlags flags) +{ + control->priv->flags = flags; +} + +void +_mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, gboolean mute) +{ + control->priv->mute = mute; +} + +void +_mate_mixer_stream_control_set_balance (MateMixerStreamControl *control, gfloat balance) +{ + control->priv->balance = balance; +} + +void +_mate_mixer_stream_control_set_fade (MateMixerStreamControl *control, gfloat fade) +{ + control->priv->fade = fade; +} diff --git a/libmatemixer/matemixer-stream-control.h b/libmatemixer/matemixer-stream-control.h index 727ed54..51d7f95 100644 --- a/libmatemixer/matemixer-stream-control.h +++ b/libmatemixer/matemixer-stream-control.h @@ -23,6 +23,7 @@ #include #include +#include G_BEGIN_DECLS @@ -32,116 +33,127 @@ G_BEGIN_DECLS # define MATE_MIXER_INFINITY G_MAXDOUBLE #endif -#define MATE_MIXER_TYPE_STREAM_CONTROL \ +#define MATE_MIXER_TYPE_STREAM_CONTROL \ (mate_mixer_stream_control_get_type ()) -#define MATE_MIXER_STREAM_CONTROL(o) \ +#define MATE_MIXER_STREAM_CONTROL(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_STREAM_CONTROL, MateMixerStreamControl)) -#define MATE_MIXER_IS_STREAM_CONTROL(o) \ +#define MATE_MIXER_IS_STREAM_CONTROL(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_STREAM_CONTROL)) -#define MATE_MIXER_STREAM_CONTROL_GET_INTERFACE(o) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_STREAM_CONTROL, MateMixerStreamControlInterface)) +#define MATE_MIXER_STREAM_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_STREAM_CONTROL, MateMixerStreamControlClass)) +#define MATE_MIXER_IS_STREAM_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_STREAM_CONTROL)) +#define MATE_MIXER_STREAM_CONTROL_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_STREAM_CONTROL, MateMixerStreamControlClass)) -typedef struct _MateMixerStreamControl MateMixerStreamControl; /* dummy object */ -typedef struct _MateMixerStreamControlInterface MateMixerStreamControlInterface; +typedef struct _MateMixerStreamControlClass MateMixerStreamControlClass; +typedef struct _MateMixerStreamControlPrivate MateMixerStreamControlPrivate; -struct _MateMixerStreamControlInterface +struct _MateMixerStreamControl { - GTypeInterface parent_iface; + GObject object; /*< private >*/ - const gchar * (*get_name) (MateMixerStreamControl *ctl); - const gchar * (*get_description) (MateMixerStreamControl *ctl); - - gboolean (*set_mute) (MateMixerStreamControl *ctl, - gboolean mute); - - guint (*get_num_channels) (MateMixerStreamControl *ctl); - - gboolean (*set_volume) (MateMixerStreamControl *ctl, - guint volume); - - gdouble (*get_decibel) (MateMixerStreamControl *ctl); - gboolean (*set_decibel) (MateMixerStreamControl *ctl, - gdouble decibel); - - gboolean (*has_channel_position) (MateMixerStreamControl *ctl, - MateMixerChannelPosition position); - MateMixerChannelPosition (*get_channel_position) (MateMixerStreamControl *ctl, - guint channel); - - guint (*get_channel_volume) (MateMixerStreamControl *ctl, - guint channel); - gboolean (*set_channel_volume) (MateMixerStreamControl *ctl, - guint channel, - guint volume); - - gdouble (*get_channel_decibel) (MateMixerStreamControl *ctl, - guint channel); - gboolean (*set_channel_decibel) (MateMixerStreamControl *ctl, - guint channel, - gdouble decibel); - - gboolean (*set_balance) (MateMixerStreamControl *ctl, - gfloat balance); + MateMixerStreamControlPrivate *priv; +}; - gboolean (*set_fade) (MateMixerStreamControl *ctl, - gfloat fade); +struct _MateMixerStreamControlClass +{ + GObjectClass parent_class; - guint (*get_min_volume) (MateMixerStreamControl *ctl); - guint (*get_max_volume) (MateMixerStreamControl *ctl); - guint (*get_normal_volume) (MateMixerStreamControl *ctl); - guint (*get_base_volume) (MateMixerStreamControl *ctl); + /*< private >*/ + gboolean (*set_mute) (MateMixerStreamControl *control, + gboolean mute); + + guint (*get_num_channels) (MateMixerStreamControl *control); + + guint (*get_volume) (MateMixerStreamControl *control); + gboolean (*set_volume) (MateMixerStreamControl *control, + guint volume); + + gdouble (*get_decibel) (MateMixerStreamControl *control); + gboolean (*set_decibel) (MateMixerStreamControl *control, + gdouble decibel); + + gboolean (*has_channel_position) (MateMixerStreamControl *control, + MateMixerChannelPosition position); + MateMixerChannelPosition (*get_channel_position) (MateMixerStreamControl *control, + guint channel); + + guint (*get_channel_volume) (MateMixerStreamControl *control, + guint channel); + gboolean (*set_channel_volume) (MateMixerStreamControl *control, + guint channel, + guint volume); + + gdouble (*get_channel_decibel) (MateMixerStreamControl *control, + guint channel); + gboolean (*set_channel_decibel) (MateMixerStreamControl *control, + guint channel, + gdouble decibel); + + gboolean (*set_balance) (MateMixerStreamControl *control, + gfloat balance); + + gboolean (*set_fade) (MateMixerStreamControl *control, + gfloat fade); + + guint (*get_min_volume) (MateMixerStreamControl *control); + guint (*get_max_volume) (MateMixerStreamControl *control); + guint (*get_normal_volume) (MateMixerStreamControl *control); + guint (*get_base_volume) (MateMixerStreamControl *control); }; GType mate_mixer_stream_control_get_type (void) G_GNUC_CONST; -const gchar * mate_mixer_stream_control_get_name (MateMixerStreamControl *ctl); -const gchar * mate_mixer_stream_control_get_description (MateMixerStreamControl *ctl); -MateMixerStreamControlFlags mate_mixer_stream_control_get_flags (MateMixerStreamControl *ctl); +const gchar * mate_mixer_stream_control_get_name (MateMixerStreamControl *control); +const gchar * mate_mixer_stream_control_get_label (MateMixerStreamControl *control); +MateMixerStreamControlFlags mate_mixer_stream_control_get_flags (MateMixerStreamControl *control); +MateMixerStreamControlRole mate_mixer_stream_control_get_role (MateMixerStreamControl *control); -gboolean mate_mixer_stream_control_get_mute (MateMixerStreamControl *ctl); -gboolean mate_mixer_stream_control_set_mute (MateMixerStreamControl *ctl, +gboolean mate_mixer_stream_control_get_mute (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, gboolean mute); -guint mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *ctl); +guint mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *control); -guint mate_mixer_stream_control_get_volume (MateMixerStreamControl *ctl); -gboolean mate_mixer_stream_control_set_volume (MateMixerStreamControl *ctl, +guint mate_mixer_stream_control_get_volume (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_volume (MateMixerStreamControl *control, guint volume); -gdouble mate_mixer_stream_control_get_decibel (MateMixerStreamControl *ctl); -gboolean mate_mixer_stream_control_set_decibel (MateMixerStreamControl *ctl, +gdouble mate_mixer_stream_control_get_decibel (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_decibel (MateMixerStreamControl *control, gdouble decibel); -gboolean mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *ctl, +gboolean mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *control, MateMixerChannelPosition position); -MateMixerChannelPosition mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *ctl, +MateMixerChannelPosition mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *control, guint channel); -guint mate_mixer_stream_control_get_channel_volume (MateMixerStreamControl *ctl, +guint mate_mixer_stream_control_get_channel_volume (MateMixerStreamControl *control, guint channel); -gboolean mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *ctl, +gboolean mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *control, guint channel, guint volume); -gdouble mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *ctl, +gdouble mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *control, guint channel); -gboolean mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *ctl, +gboolean mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *control, guint channel, gdouble decibel); -gfloat mate_mixer_stream_control_get_balance (MateMixerStreamControl *ctl); -gboolean mate_mixer_stream_control_set_balance (MateMixerStreamControl *ctl, +gfloat mate_mixer_stream_control_get_balance (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_balance (MateMixerStreamControl *control, gfloat balance); -gfloat mate_mixer_stream_control_get_fade (MateMixerStreamControl *ctl); -gboolean mate_mixer_stream_control_set_fade (MateMixerStreamControl *ctl, +gfloat mate_mixer_stream_control_get_fade (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_fade (MateMixerStreamControl *control, gfloat fade); -guint mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *ctl); -guint mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *ctl); -guint mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *ctl); -guint mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *ctl); +guint mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *control); +guint mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *control); +guint mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *control); +guint mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *control); G_END_DECLS diff --git a/libmatemixer/matemixer-stream.c b/libmatemixer/matemixer-stream.c index 888fddb..1902de3 100644 --- a/libmatemixer/matemixer-stream.c +++ b/libmatemixer/matemixer-stream.c @@ -15,20 +15,44 @@ * License along with this library; if not, see . */ +#include #include #include #include "matemixer-device.h" #include "matemixer-enums.h" #include "matemixer-enum-types.h" -#include "matemixer-port.h" #include "matemixer-stream.h" +#include "matemixer-stream-control.h" +#include "matemixer-switch.h" /** * SECTION:matemixer-stream * @include: libmatemixer/matemixer.h */ +struct _MateMixerStreamPrivate +{ + gchar *name; + GList *controls; + GList *switches; + gboolean monitor_enabled; + MateMixerDevice *device; + MateMixerStreamFlags flags; + MateMixerStreamState state; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_DEVICE, + PROP_FLAGS, + PROP_STATE, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + enum { MONITOR_VALUE, N_SIGNALS @@ -36,245 +60,285 @@ enum { static guint signals[N_SIGNALS] = { 0, }; -G_DEFINE_INTERFACE (MateMixerStream, mate_mixer_stream, G_TYPE_OBJECT) +static void mate_mixer_stream_class_init (MateMixerStreamClass *klass); + +static void mate_mixer_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_stream_init (MateMixerStream *stream); +static void mate_mixer_stream_dispose (GObject *object); +static void mate_mixer_stream_finalize (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (MateMixerStream, mate_mixer_stream, G_TYPE_OBJECT) + +static MateMixerStreamControl *mate_mixer_stream_real_get_control (MateMixerStream *stream, + const gchar *name); +static MateMixerSwitch * mate_mixer_stream_real_get_switch (MateMixerStream *stream, + const gchar *name); static void -mate_mixer_stream_default_init (MateMixerStreamInterface *iface) +mate_mixer_stream_class_init (MateMixerStreamClass *klass) { - g_object_interface_install_property (iface, - g_param_spec_string ("name", - "Name", - "Name of the stream", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_string ("description", - "Description", - "Description of the stream", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_object ("device", - "Device", - "Device the stream belongs to", - MATE_MIXER_TYPE_DEVICE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_flags ("flags", - "Flags", - "Capability flags of the stream", - MATE_MIXER_TYPE_STREAM_FLAGS, - MATE_MIXER_STREAM_NO_FLAGS, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_enum ("state", - "State", - "Current state of the stream", - MATE_MIXER_TYPE_STREAM_STATE, - MATE_MIXER_STREAM_STATE_UNKNOWN, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - - g_object_interface_install_property (iface, - g_param_spec_object ("active-port", - "Active port", - "The currently active port of the stream", - MATE_MIXER_TYPE_PORT, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + GObjectClass *object_class; + + klass->get_control = mate_mixer_stream_real_get_control; + klass->get_switch = mate_mixer_stream_real_get_switch; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = mate_mixer_stream_dispose; + object_class->finalize = mate_mixer_stream_finalize; + object_class->get_property = mate_mixer_stream_get_property; + object_class->set_property = mate_mixer_stream_set_property; + + properties[PROP_NAME] = + g_param_spec_string ("name", + "Name", + "Name of the stream", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_DEVICE] = + g_param_spec_object ("device", + "Device", + "Device the stream belongs to", + MATE_MIXER_TYPE_DEVICE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_FLAGS] = + g_param_spec_flags ("flags", + "Flags", + "Capability flags of the stream", + MATE_MIXER_TYPE_STREAM_FLAGS, + MATE_MIXER_STREAM_NO_FLAGS, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE] = + g_param_spec_enum ("state", + "State", + "Current state of the stream", + MATE_MIXER_TYPE_STREAM_STATE, + MATE_MIXER_STREAM_STATE_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); signals[MONITOR_VALUE] = g_signal_new ("monitor-value", - G_TYPE_FROM_INTERFACE (iface), + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerStreamInterface, monitor_value), + G_STRUCT_OFFSET (MateMixerStreamClass, monitor_value), NULL, NULL, g_cclosure_marshal_VOID__DOUBLE, G_TYPE_NONE, 1, G_TYPE_DOUBLE); + + g_type_class_add_private (object_class, sizeof (MateMixerStreamPrivate)); } -const gchar * -mate_mixer_stream_get_name (MateMixerStream *stream) +static void +mate_mixer_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) { - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - - /* Implementation required */ - return MATE_MIXER_STREAM_GET_INTERFACE (stream)->get_name (stream); + MateMixerStream *stream; + + stream = MATE_MIXER_STREAM (object); + + switch (param_id) { + case PROP_NAME: + g_value_set_string (value, stream->priv->name); + break; + case PROP_DEVICE: + g_value_set_object (value, stream->priv->device); + break; + case PROP_FLAGS: + g_value_set_flags (value, stream->priv->flags); + break; + case PROP_STATE: + g_value_set_enum (value, stream->priv->state); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } } -const gchar * -mate_mixer_stream_get_description (MateMixerStream *stream) +static void +mate_mixer_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) { - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - - /* Implementation required */ - return MATE_MIXER_STREAM_GET_INTERFACE (stream)->get_description (stream); + MateMixerStream *stream; + + stream = MATE_MIXER_STREAM (object); + + switch (param_id) { + case PROP_NAME: + /* Construct-only string */ + stream->priv->name = g_value_dup_string (value); + break; + case PROP_DEVICE: + /* Construct-only object */ + stream->priv->device = g_value_dup_object (value); + break; + case PROP_FLAGS: + stream->priv->flags = g_value_get_flags (value); + break; + case PROP_STATE: + stream->priv->state = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } } -MateMixerDevice * -mate_mixer_stream_get_device (MateMixerStream *stream) +static void +mate_mixer_stream_init (MateMixerStream *stream) { - MateMixerDevice *device = NULL; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - - g_object_get (G_OBJECT (stream), - "device", &device, - NULL); - - if (device != NULL) - g_object_unref (device); - - return device; - + stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, + MATE_MIXER_TYPE_STREAM, + MateMixerStreamPrivate); } -MateMixerStreamFlags -mate_mixer_stream_get_flags (MateMixerStream *stream) +static void +mate_mixer_stream_dispose (GObject *object) { - MateMixerStreamFlags flags = MATE_MIXER_STREAM_NO_FLAGS; + MateMixerStream *stream; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS); + stream = MATE_MIXER_STREAM (object); - g_object_get (G_OBJECT (stream), - "flags", &flags, - NULL); + g_clear_object (&stream->priv->device); - return flags; + G_OBJECT_CLASS (mate_mixer_stream_parent_class)->dispose (object); } -MateMixerStreamState -mate_mixer_stream_get_state (MateMixerStream *stream) +static void +mate_mixer_stream_finalize (GObject *object) { - MateMixerStreamState state = MATE_MIXER_STREAM_STATE_UNKNOWN; + MateMixerStream *stream; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN); + stream = MATE_MIXER_STREAM (object); - g_object_get (G_OBJECT (stream), - "state", &state, - NULL); + g_free (stream->priv->name); - return state; + G_OBJECT_CLASS (mate_mixer_stream_parent_class)->finalize (object); } -MateMixerStreamControl * -mate_mixer_stream_get_control (MateMixerStream *stream, const gchar *name) +const gchar * +mate_mixer_stream_get_name (MateMixerStream *stream) { g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - /* Implementation required */ - return MATE_MIXER_STREAM_GET_INTERFACE (stream)->get_control (stream, name); + return stream->priv->name; } -MateMixerStreamControl * -mate_mixer_stream_get_default_control (MateMixerStream *stream) +MateMixerDevice * +mate_mixer_stream_get_device (MateMixerStream *stream) { g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - /* Implementation required */ - return MATE_MIXER_STREAM_GET_INTERFACE (stream)->get_default_control (stream); + return stream->priv->device; } -MateMixerPort * -mate_mixer_stream_get_port (MateMixerStream *stream, const gchar *name) +MateMixerStreamFlags +mate_mixer_stream_get_flags (MateMixerStream *stream) { - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + return stream->priv->flags; +} - if (iface->get_port != NULL) - return iface->get_port (stream, name); +MateMixerStreamState +mate_mixer_stream_get_state (MateMixerStream *stream) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN); - return FALSE; + return stream->priv->state; } -MateMixerPort * -mate_mixer_stream_get_active_port (MateMixerStream *stream) +MateMixerStreamControl * +mate_mixer_stream_get_control (MateMixerStream *stream, const gchar *name) { - MateMixerPort *port = NULL; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); + g_return_val_if_fail (name != NULL, NULL); - g_object_get (G_OBJECT (stream), - "active-port", &port, - NULL); - - if (port != NULL) - g_object_unref (port); - - return port; + return MATE_MIXER_STREAM_GET_CLASS (stream)->get_control (stream, name); } -gboolean -mate_mixer_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) +MateMixerSwitch * +mate_mixer_stream_get_switch (MateMixerStream *stream, const gchar *name) { - MateMixerStreamInterface *iface; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); + g_return_val_if_fail (name != NULL, NULL); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + return MATE_MIXER_STREAM_GET_CLASS (stream)->get_switch (stream, name); +} - if (iface->set_active_port != NULL) - return iface->set_active_port (stream, port); +MateMixerStreamControl * +mate_mixer_stream_get_default_control (MateMixerStream *stream) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - return FALSE; + return MATE_MIXER_STREAM_GET_CLASS (stream)->get_default_control (stream); } const GList * mate_mixer_stream_list_controls (MateMixerStream *stream) { - MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); - - if (iface->list_controls != NULL) - return iface->list_controls (stream); + if (stream->priv->controls == NULL) + stream->priv->controls = MATE_MIXER_STREAM_GET_CLASS (stream)->list_controls (stream); - return NULL; + return (const GList *) stream->priv->controls; } const GList * -mate_mixer_stream_list_ports (MateMixerStream *stream) +mate_mixer_stream_list_switches (MateMixerStream *stream) { - MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + if (stream->priv->switches == NULL) { + MateMixerStreamClass *klass = MATE_MIXER_STREAM_GET_CLASS (stream); - if (iface->list_ports != NULL) - return iface->list_ports (stream); + if (klass->list_switches != NULL) + stream->priv->switches = klass->list_switches (stream); + } - return NULL; + return (const GList *) stream->priv->switches; } gboolean mate_mixer_stream_suspend (MateMixerStream *stream) { - MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + if (stream->priv->state == MATE_MIXER_STREAM_STATE_SUSPENDED) + return TRUE; - if (iface->suspend != NULL) - return iface->suspend (stream); + if (stream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND) + return MATE_MIXER_STREAM_GET_CLASS (stream)->suspend (stream); return FALSE; } @@ -282,14 +346,13 @@ mate_mixer_stream_suspend (MateMixerStream *stream) gboolean mate_mixer_stream_resume (MateMixerStream *stream) { - MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + if (stream->priv->state != MATE_MIXER_STREAM_STATE_SUSPENDED) + return TRUE; - if (iface->resume != NULL) - return iface->resume (stream); + if (stream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND) + return MATE_MIXER_STREAM_GET_CLASS (stream)->resume (stream); return FALSE; } @@ -297,58 +360,65 @@ mate_mixer_stream_resume (MateMixerStream *stream) gboolean mate_mixer_stream_monitor_get_enabled (MateMixerStream *stream) { - gboolean enabled = FALSE; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - g_object_get (G_OBJECT (stream), - "monitor-enabled", &enabled, - NULL); - - return enabled; + return stream->priv->monitor_enabled; } gboolean mate_mixer_stream_monitor_set_enabled (MateMixerStream *stream, gboolean enabled) { - MateMixerStreamInterface *iface; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + if (stream->priv->monitor_enabled == enabled) + return TRUE; - if (iface->monitor_set_enabled != NULL) - return iface->monitor_set_enabled (stream, enabled); + if (stream->priv->flags & MATE_MIXER_STREAM_HAS_MONITOR) { + if (enabled) + return MATE_MIXER_STREAM_GET_CLASS (stream)->monitor_start (stream); + else + return MATE_MIXER_STREAM_GET_CLASS (stream)->monitor_stop (stream); + } return FALSE; } -const gchar * -mate_mixer_stream_monitor_get_name (MateMixerStream *stream) +static MateMixerStreamControl * +mate_mixer_stream_real_get_control (MateMixerStream *stream, const gchar *name) { - MateMixerStreamInterface *iface; + const GList *list; g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); + g_return_val_if_fail (name != NULL, NULL); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + list = mate_mixer_stream_list_controls (stream); + while (list != NULL) { + MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (list->data); - if (iface->monitor_get_name != NULL) - return iface->monitor_get_name (stream); + if (strcmp (name, mate_mixer_stream_control_get_name (control)) == 0) + return control; - return FALSE; + list = list->next; + } + return NULL; } -gboolean -mate_mixer_stream_monitor_set_name (MateMixerStream *stream, const gchar *name) +static MateMixerSwitch * +mate_mixer_stream_real_get_switch (MateMixerStream *stream, const gchar *name) { - MateMixerStreamInterface *iface; + const GList *list; - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); + g_return_val_if_fail (name != NULL, NULL); - iface = MATE_MIXER_STREAM_GET_INTERFACE (stream); + list = mate_mixer_stream_list_switches (stream); + while (list != NULL) { + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (list->data); - if (iface->monitor_set_name != NULL) - return iface->monitor_set_name (stream, name); + if (strcmp (name, mate_mixer_switch_get_name (swtch)) == 0) + return swtch; - return FALSE; + list = list->next; + } + return NULL; } diff --git a/libmatemixer/matemixer-stream.h b/libmatemixer/matemixer-stream.h index aecf40e..e73579e 100644 --- a/libmatemixer/matemixer-stream.h +++ b/libmatemixer/matemixer-stream.h @@ -21,88 +21,77 @@ #include #include -#include #include -#include -#include +#include G_BEGIN_DECLS -#ifdef INFINITY -#define MATE_MIXER_INFINITY INFINITY -#else -#define MATE_MIXER_INFINITY G_MAXDOUBLE -#endif - #define MATE_MIXER_TYPE_STREAM \ (mate_mixer_stream_get_type ()) #define MATE_MIXER_STREAM(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_STREAM, MateMixerStream)) #define MATE_MIXER_IS_STREAM(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_STREAM)) -#define MATE_MIXER_STREAM_GET_INTERFACE(o) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_STREAM, MateMixerStreamInterface)) +#define MATE_MIXER_STREAM_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_STREAM, MateMixerStreamClass)) +#define MATE_MIXER_IS_STREAM_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_STREAM)) +#define MATE_MIXER_STREAM_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_STREAM, MateMixerStreamClass)) -typedef struct _MateMixerStream MateMixerStream; /* dummy object */ -typedef struct _MateMixerStreamInterface MateMixerStreamInterface; +typedef struct _MateMixerStreamClass MateMixerStreamClass; +typedef struct _MateMixerStreamPrivate MateMixerStreamPrivate; -struct _MateMixerStreamInterface +struct _MateMixerStream { - GTypeInterface parent_iface; + GObject object; /*< private >*/ - const gchar * (*get_name) (MateMixerStream *stream); - const gchar * (*get_description) (MateMixerStream *stream); + MateMixerStreamPrivate *priv; +}; + +struct _MateMixerStreamClass +{ + GObjectClass parent_class; + /*< private >*/ MateMixerStreamControl *(*get_control) (MateMixerStream *stream, const gchar *name); + MateMixerStreamControl *(*get_default_control) (MateMixerStream *stream); - MateMixerPort * (*get_port) (MateMixerStream *stream, + MateMixerSwitch *(*get_switch) (MateMixerStream *stream, const gchar *name); - gboolean (*set_active_port) (MateMixerStream *stream, - MateMixerPort *port); - - const GList * (*list_controls) (MateMixerStream *stream); - const GList * (*list_ports) (MateMixerStream *stream); + GList *(*list_controls) (MateMixerStream *stream); + GList *(*list_switches) (MateMixerStream *stream); gboolean (*suspend) (MateMixerStream *stream); gboolean (*resume) (MateMixerStream *stream); - gboolean (*monitor_set_enabled) (MateMixerStream *stream, - gboolean enabled); - - const gchar * (*monitor_get_name) (MateMixerStream *stream); - gboolean (*monitor_set_name) (MateMixerStream *stream, - const gchar *name); + gboolean (*monitor_start) (MateMixerStream *stream); + gboolean (*monitor_stop) (MateMixerStream *stream); /* Signals */ - void (*monitor_value) (MateMixerStream *stream, - gdouble value); + void (*monitor_value) (MateMixerStream *stream, gdouble value); }; GType mate_mixer_stream_get_type (void) G_GNUC_CONST; const gchar * mate_mixer_stream_get_name (MateMixerStream *stream); -const gchar * mate_mixer_stream_get_description (MateMixerStream *stream); MateMixerDevice * mate_mixer_stream_get_device (MateMixerStream *stream); MateMixerStreamFlags mate_mixer_stream_get_flags (MateMixerStream *stream); MateMixerStreamState mate_mixer_stream_get_state (MateMixerStream *stream); MateMixerStreamControl *mate_mixer_stream_get_control (MateMixerStream *stream, const gchar *name); -MateMixerStreamControl *mate_mixer_stream_get_default_control (MateMixerStream *stream); - -MateMixerPort * mate_mixer_stream_get_port (MateMixerStream *stream, +MateMixerSwitch * mate_mixer_stream_get_switch (MateMixerStream *stream, const gchar *name); -MateMixerPort * mate_mixer_stream_get_active_port (MateMixerStream *stream); -gboolean mate_mixer_stream_set_active_port (MateMixerStream *stream, - MateMixerPort *port); +MateMixerStreamControl *mate_mixer_stream_get_default_control (MateMixerStream *stream); const GList * mate_mixer_stream_list_controls (MateMixerStream *stream); -const GList * mate_mixer_stream_list_ports (MateMixerStream *stream); +const GList * mate_mixer_stream_list_switches (MateMixerStream *stream); gboolean mate_mixer_stream_suspend (MateMixerStream *stream); gboolean mate_mixer_stream_resume (MateMixerStream *stream); @@ -111,10 +100,6 @@ gboolean mate_mixer_stream_monitor_get_enabled (MateMixerStream * gboolean mate_mixer_stream_monitor_set_enabled (MateMixerStream *stream, gboolean enabled); -const gchar * mate_mixer_stream_monitor_get_name (MateMixerStream *stream); -gboolean mate_mixer_stream_monitor_set_name (MateMixerStream *stream, - const gchar *name); - G_END_DECLS #endif /* MATEMIXER_STREAM_H */ diff --git a/libmatemixer/matemixer-switch-option-private.h b/libmatemixer/matemixer-switch-option-private.h new file mode 100644 index 0000000..dea7583 --- /dev/null +++ b/libmatemixer/matemixer-switch-option-private.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_SWITCH_OPTION_PRIVATE_H +#define MATEMIXER_SWITCH_OPTION_PRIVATE_H + +#include + +#include "matemixer-switch-option.h" + +G_BEGIN_DECLS + +MateMixerSwitchOption *_mate_mixer_switch_option_new (const gchar *name, + const gchar *label, + const gchar *icon); + +G_END_DECLS + +#endif /* MATEMIXER_SWITCH_OPTION_PRIVATE_H */ diff --git a/libmatemixer/matemixer-switch-option.c b/libmatemixer/matemixer-switch-option.c new file mode 100644 index 0000000..e924b46 --- /dev/null +++ b/libmatemixer/matemixer-switch-option.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include + +#include "matemixer-switch-option.h" +#include "matemixer-switch-option-private.h" + +/** + * SECTION:matemixer-stream-switch-option + * @include: libmatemixer/matemixer.h + */ + +struct _MateMixerSwitchOptionPrivate +{ + gchar *name; + gchar *label; + gchar *icon; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_LABEL, + PROP_ICON, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void mate_mixer_switch_option_class_init (MateMixerSwitchOptionClass *klass); + +static void mate_mixer_switch_option_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_switch_option_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_switch_option_init (MateMixerSwitchOption *option); +static void mate_mixer_switch_option_finalize (GObject *object); + +G_DEFINE_TYPE (MateMixerSwitchOption, mate_mixer_switch_option, G_TYPE_OBJECT) + +static void +mate_mixer_switch_option_class_init (MateMixerSwitchOptionClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = mate_mixer_switch_option_finalize; + object_class->get_property = mate_mixer_switch_option_get_property; + object_class->set_property = mate_mixer_switch_option_set_property; + + properties[PROP_NAME] = + g_param_spec_string ("name", + "Name", + "Name of the switch option", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_LABEL] = + g_param_spec_string ("label", + "Label", + "Label of the switch option", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ICON] = + g_param_spec_string ("icon", + "Icon", + "Icon of the switch option", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (object_class, sizeof (MateMixerSwitchOptionPrivate)); +} + +static void +mate_mixer_switch_option_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerSwitchOption *option; + + option = MATE_MIXER_SWITCH_OPTION (object); + + switch (param_id) { + case PROP_NAME: + g_value_set_string (value, option->priv->name); + break; + case PROP_LABEL: + g_value_set_string (value, option->priv->label); + break; + case PROP_ICON: + g_value_set_string (value, option->priv->icon); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_switch_option_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerSwitchOption *option; + + option = MATE_MIXER_SWITCH_OPTION (object); + + switch (param_id) { + case PROP_NAME: + /* Construct-only string */ + option->priv->name = g_value_dup_string (value); + break; + case PROP_LABEL: + /* Construct-only string */ + option->priv->label = g_value_dup_string (value); + break; + case PROP_ICON: + /* Construct-only string */ + option->priv->icon = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_switch_option_init (MateMixerSwitchOption *option) +{ + option->priv = G_TYPE_INSTANCE_GET_PRIVATE (option, + MATE_MIXER_TYPE_SWITCH_OPTION, + MateMixerSwitchOptionPrivate); +} + +static void +mate_mixer_switch_option_finalize (GObject *object) +{ + MateMixerSwitchOption *option; + + option = MATE_MIXER_SWITCH_OPTION (object); + + g_free (option->priv->name); + g_free (option->priv->label); + g_free (option->priv->icon); + + G_OBJECT_CLASS (mate_mixer_switch_option_parent_class)->finalize (object); +} + +/** + * mate_mixer_switch_option_get_name: + */ +const gchar * +mate_mixer_switch_option_get_name (MateMixerSwitchOption *option) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH_OPTION (option), NULL); + + return option->priv->name; +} + +/** + * mate_mixer_switch_option_get_label: + */ +const gchar * +mate_mixer_switch_option_get_label (MateMixerSwitchOption *option) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH_OPTION (option), NULL); + + return option->priv->label; +} + +/** + * mate_mixer_switch_option_get_icon: + */ +const gchar * +mate_mixer_switch_option_get_icon (MateMixerSwitchOption *option) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH_OPTION (option), NULL); + + return option->priv->icon; +} + +MateMixerSwitchOption * +_mate_mixer_switch_option_new (const gchar *name, + const gchar *label, + const gchar *icon) +{ + return g_object_new (MATE_MIXER_TYPE_SWITCH_OPTION, + "name", name, + "label", label, + "icon", icon, + NULL); +} diff --git a/libmatemixer/matemixer-switch-option.h b/libmatemixer/matemixer-switch-option.h new file mode 100644 index 0000000..b02c42c --- /dev/null +++ b/libmatemixer/matemixer-switch-option.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_SWITCH_OPTION_H +#define MATEMIXER_SWITCH_OPTION_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define MATE_MIXER_TYPE_SWITCH_OPTION \ + (mate_mixer_switch_option_get_type ()) +#define MATE_MIXER_SWITCH_OPTION(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_SWITCH_OPTION, MateMixerSwitchOption)) +#define MATE_MIXER_IS_SWITCH_OPTION(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_SWITCH_OPTION)) +#define MATE_MIXER_SWITCH_OPTION_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_SWITCH_OPTION, MateMixerSwitchOptionClass)) +#define MATE_MIXER_IS_SWITCH_OPTION_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_SWITCH_OPTION)) +#define MATE_MIXER_SWITCH_OPTION_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_SWITCH_OPTION, MateMixerSwitchOptionClass)) + +typedef struct _MateMixerSwitchOptionClass MateMixerSwitchOptionClass; +typedef struct _MateMixerSwitchOptionPrivate MateMixerSwitchOptionPrivate; + +struct _MateMixerSwitchOption +{ + GObject parent; + + /*< private >*/ + MateMixerSwitchOptionPrivate *priv; +}; + +struct _MateMixerSwitchOptionClass +{ + GObjectClass parent_class; +}; + +GType mate_mixer_switch_option_get_type (void) G_GNUC_CONST; + +const gchar *mate_mixer_switch_option_get_name (MateMixerSwitchOption *option); +const gchar *mate_mixer_switch_option_get_label (MateMixerSwitchOption *option); +const gchar *mate_mixer_switch_option_get_icon (MateMixerSwitchOption *option); + +G_END_DECLS + +#endif /* MATEMIXER_SWITCH_OPTION_H */ diff --git a/libmatemixer/matemixer-switch-private.h b/libmatemixer/matemixer-switch-private.h new file mode 100644 index 0000000..42390a9 --- /dev/null +++ b/libmatemixer/matemixer-switch-private.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_SWITCH_PRIVATE_H +#define MATEMIXER_SWITCH_PRIVATE_H + +#include +#include "matemixer-types.h" + +G_BEGIN_DECLS + +void _mate_mixer_switch_set_active_option (MateMixerSwitch *sw, + MateMixerSwitchOption *option); + +G_END_DECLS + +#endif /* MATEMIXER_SWITCH_PRIVATE_H */ diff --git a/libmatemixer/matemixer-switch.c b/libmatemixer/matemixer-switch.c new file mode 100644 index 0000000..b30e405 --- /dev/null +++ b/libmatemixer/matemixer-switch.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include "matemixer-enums.h" +#include "matemixer-enum-types.h" +#include "matemixer-switch.h" +#include "matemixer-switch-private.h" +#include "matemixer-switch-option.h" + +/** + * SECTION:matemixer-switch + * @include: libmatemixer/matemixer.h + */ + +struct _MateMixerSwitchPrivate +{ + gchar *name; + gchar *label; + GList *options; + MateMixerSwitchOption *active; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_LABEL, + PROP_ACTIVE_OPTION, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void mate_mixer_switch_class_init (MateMixerSwitchClass *klass); + +static void mate_mixer_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_switch_init (MateMixerSwitch *swtch); +static void mate_mixer_switch_finalize (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (MateMixerSwitch, mate_mixer_switch, G_TYPE_OBJECT) + +static MateMixerSwitchOption *mate_mixer_switch_real_get_option (MateMixerSwitch *swtch, + const gchar *name); + +static void +mate_mixer_switch_class_init (MateMixerSwitchClass *klass) +{ + GObjectClass *object_class; + + klass->get_option = mate_mixer_switch_real_get_option; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = mate_mixer_switch_finalize; + object_class->get_property = mate_mixer_switch_get_property; + object_class->set_property = mate_mixer_switch_set_property; + + properties[PROP_NAME] = + g_param_spec_string ("name", + "Name", + "Name of the switch", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_LABEL] = + g_param_spec_string ("label", + "Label", + "Label of the switch", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ACTIVE_OPTION] = + g_param_spec_object ("active-option", + "Active option", + "Active option of the switch", + MATE_MIXER_TYPE_SWITCH_OPTION, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (object_class, sizeof (MateMixerSwitchPrivate)); +} + +static void +mate_mixer_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerSwitch *swtch; + + swtch = MATE_MIXER_SWITCH (object); + + switch (param_id) { + case PROP_NAME: + g_value_set_string (value, swtch->priv->name); + break; + case PROP_LABEL: + g_value_set_string (value, swtch->priv->label); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerSwitch *swtch; + + swtch = MATE_MIXER_SWITCH (object); + + switch (param_id) { + case PROP_NAME: + /* Construct-only string */ + swtch->priv->name = g_value_dup_string (value); + break; + case PROP_LABEL: + /* Construct-only string */ + swtch->priv->label = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_switch_init (MateMixerSwitch *swtch) +{ + swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch, + MATE_MIXER_TYPE_SWITCH, + MateMixerSwitchPrivate); +} + +static void +mate_mixer_switch_finalize (GObject *object) +{ + MateMixerSwitch *swtch; + + swtch = MATE_MIXER_SWITCH (object); + + g_free (swtch->priv->name); + g_free (swtch->priv->label); + + G_OBJECT_CLASS (mate_mixer_switch_parent_class)->finalize (object); +} + +const gchar * +mate_mixer_switch_get_name (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), NULL); + + return swtch->priv->name; +} + +const gchar * +mate_mixer_switch_get_label (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), NULL); + + return swtch->priv->label; +} + +MateMixerSwitchOption * +mate_mixer_switch_get_option (MateMixerSwitch *swtch, const gchar *name) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), NULL); + + return MATE_MIXER_SWITCH_GET_CLASS (swtch)->get_option (swtch, name); +} + +MateMixerSwitchOption * +mate_mixer_switch_get_active_option (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), NULL); + + return swtch->priv->active; +} + +gboolean +mate_mixer_switch_set_active_option (MateMixerSwitch *swtch, + MateMixerSwitchOption *option) +{ + MateMixerSwitchClass *klass; + + g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), FALSE); + + klass = MATE_MIXER_SWITCH_GET_CLASS (swtch); + if (klass->set_active_option != NULL) { + if (klass->set_active_option (swtch, option) == FALSE) + return FALSE; + + _mate_mixer_switch_set_active_option (swtch, option); + return TRUE; + } + return FALSE; +} + +const GList * +mate_mixer_switch_list_options (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), NULL); + + if (swtch->priv->options == NULL) { + MateMixerSwitchClass *klass = MATE_MIXER_SWITCH_GET_CLASS (swtch); + + if (klass->list_options != NULL) + swtch->priv->options = klass->list_options (swtch); + } + return (const GList *) swtch->priv->options; +} + +void +_mate_mixer_switch_set_active_option (MateMixerSwitch *swtch, + MateMixerSwitchOption *option) +{ + g_return_if_fail (MATE_MIXER_IS_SWITCH (swtch)); + g_return_if_fail (MATE_MIXER_IS_SWITCH_OPTION (option)); + + if (swtch->priv->active == option) + return; + + if (swtch->priv->active != NULL) + g_object_unref (swtch->priv->active); + + swtch->priv->active = g_object_ref (option); + + g_object_notify_by_pspec (G_OBJECT (swtch), properties[PROP_ACTIVE_OPTION]); +} + +static MateMixerSwitchOption * +mate_mixer_switch_real_get_option (MateMixerSwitch *swtch, const gchar *name) +{ + const GList *list; + + g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), NULL); + g_return_val_if_fail (name != NULL, NULL); + + list = mate_mixer_switch_list_options (swtch); + while (list != NULL) { + MateMixerSwitchOption *option = MATE_MIXER_SWITCH_OPTION (list->data); + + if (strcmp (name, mate_mixer_switch_option_get_name (option)) == 0) + return option; + + list = list->next; + } + return NULL; +} diff --git a/libmatemixer/matemixer-switch.h b/libmatemixer/matemixer-switch.h new file mode 100644 index 0000000..3035607 --- /dev/null +++ b/libmatemixer/matemixer-switch.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_SWITCH_H +#define MATEMIXER_SWITCH_H + +#include +#include + +#include + +G_BEGIN_DECLS + +#define MATE_MIXER_TYPE_SWITCH \ + (mate_mixer_switch_get_type ()) +#define MATE_MIXER_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_SWITCH, MateMixerSwitch)) +#define MATE_MIXER_IS_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_SWITCH)) +#define MATE_MIXER_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_SWITCH, MateMixerSwitchClass)) +#define MATE_MIXER_IS_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_SWITCH)) +#define MATE_MIXER_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_SWITCH, MateMixerSwitchClass)) + +typedef struct _MateMixerSwitchClass MateMixerSwitchClass; +typedef struct _MateMixerSwitchPrivate MateMixerSwitchPrivate; + +struct _MateMixerSwitch +{ + GObject object; + + /*< private >*/ + MateMixerSwitchPrivate *priv; +}; + +struct _MateMixerSwitchClass +{ + GObjectClass parent_class; + + /*< private >*/ + MateMixerSwitchOption *(*get_option) (MateMixerSwitch *swtch, + const gchar *name); + + gboolean (*set_active_option) (MateMixerSwitch *swtch, + MateMixerSwitchOption *option); + + GList *(*list_options) (MateMixerSwitch *swtch); +}; + +GType mate_mixer_switch_get_type (void) G_GNUC_CONST; + +const gchar * mate_mixer_switch_get_name (MateMixerSwitch *swtch); +const gchar * mate_mixer_switch_get_label (MateMixerSwitch *swtch); + +MateMixerSwitchOption *mate_mixer_switch_get_option (MateMixerSwitch *swtch, + const gchar *name); + +MateMixerSwitchOption *mate_mixer_switch_get_active_option (MateMixerSwitch *swtch); +gboolean mate_mixer_switch_set_active_option (MateMixerSwitch *swtch, + MateMixerSwitchOption *option); + +const GList * mate_mixer_switch_list_options (MateMixerSwitch *swtch); + +G_END_DECLS + +#endif /* MATEMIXER_SWITCH_H */ diff --git a/libmatemixer/matemixer-toggle.c b/libmatemixer/matemixer-toggle.c new file mode 100644 index 0000000..2eea599 --- /dev/null +++ b/libmatemixer/matemixer-toggle.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include "matemixer-switch.h" +#include "matemixer-switch-option.h" +#include "matemixer-toggle.h" + +/** + * SECTION:matemixer-toggle + * @include: libmatemixer/matemixer.h + */ + +struct _MateMixerTogglePrivate +{ + MateMixerSwitchOption *on; + MateMixerSwitchOption *off; +}; + +enum { + PROP_0, + PROP_STATE, + PROP_STATE_OPTION_ON, + PROP_STATE_OPTION_OFF, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void mate_mixer_toggle_class_init (MateMixerToggleClass *klass); + +static void mate_mixer_toggle_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void mate_mixer_toggle_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void mate_mixer_toggle_init (MateMixerToggle *toggle); +static void mate_mixer_toggle_dispose (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (MateMixerToggle, mate_mixer_toggle, MATE_MIXER_TYPE_SWITCH) + +static MateMixerSwitchOption *mate_mixer_toggle_get_option (MateMixerSwitch *swtch, + const gchar *name); + +static GList * mate_mixer_toggle_list_options (MateMixerSwitch *swtch); + +static void +mate_mixer_toggle_class_init (MateMixerToggleClass *klass) +{ + GObjectClass *object_class; + MateMixerSwitchClass *switch_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = mate_mixer_toggle_dispose; + object_class->get_property = mate_mixer_toggle_get_property; + object_class->set_property = mate_mixer_toggle_set_property; + + switch_class = MATE_MIXER_SWITCH_CLASS (klass); + switch_class->get_option = mate_mixer_toggle_get_option; + switch_class->list_options = mate_mixer_toggle_list_options; + + properties[PROP_STATE] = + g_param_spec_boolean ("state", + "State", + "Current state", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE_OPTION_ON] = + g_param_spec_object ("state-option-on", + "State option for on", + "Option corresponding to the 'on' value of the toggle", + MATE_MIXER_TYPE_SWITCH_OPTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE_OPTION_OFF] = + g_param_spec_object ("state-option-off", + "State option for off", + "Option corresponding to the 'off' value of the toggle", + MATE_MIXER_TYPE_SWITCH_OPTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (object_class, sizeof (MateMixerTogglePrivate)); +} + +static void +mate_mixer_toggle_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + MateMixerToggle *toggle; + + toggle = MATE_MIXER_TOGGLE (object); + + switch (param_id) { + case PROP_STATE: + g_value_set_boolean (value, mate_mixer_toggle_get_state (toggle)); + break; + case PROP_STATE_OPTION_ON: + g_value_set_object (value, toggle->priv->on); + break; + case PROP_STATE_OPTION_OFF: + g_value_set_object (value, toggle->priv->off); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_toggle_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + MateMixerToggle *toggle; + + toggle = MATE_MIXER_TOGGLE (object); + + switch (param_id) { + case PROP_STATE_OPTION_ON: + /* Construct-only object */ + toggle->priv->on = g_value_dup_object (value); + break; + case PROP_STATE_OPTION_OFF: + /* Construct-only object */ + toggle->priv->off = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +mate_mixer_toggle_init (MateMixerToggle *toggle) +{ + toggle->priv = G_TYPE_INSTANCE_GET_PRIVATE (toggle, + MATE_MIXER_TYPE_TOGGLE, + MateMixerTogglePrivate); +} + +static void +mate_mixer_toggle_dispose (GObject *object) +{ + MateMixerToggle *toggle; + + toggle = MATE_MIXER_TOGGLE (object); + + g_clear_object (&toggle->priv->on); + g_clear_object (&toggle->priv->off); + + G_OBJECT_CLASS (mate_mixer_toggle_parent_class)->dispose (object); +} + +gboolean +mate_mixer_toggle_get_state (MateMixerToggle *toggle) +{ + MateMixerSwitchOption *active; + + g_return_val_if_fail (MATE_MIXER_IS_TOGGLE (toggle), FALSE); + + active = mate_mixer_switch_get_active_option (MATE_MIXER_SWITCH (toggle)); + if (active == toggle->priv->on) + return TRUE; + else + return FALSE; +} + +MateMixerSwitchOption * +mate_mixer_toggle_get_state_option (MateMixerToggle *toggle, gboolean state) +{ + g_return_val_if_fail (MATE_MIXER_IS_TOGGLE (toggle), NULL); + + if (state == TRUE) + return toggle->priv->on; + else + return toggle->priv->off; +} + +gboolean +mate_mixer_toggle_set_state (MateMixerToggle *toggle, gboolean state) +{ + MateMixerSwitchOption *active; + + g_return_val_if_fail (MATE_MIXER_IS_TOGGLE (toggle), FALSE); + + if (state == TRUE) + active = toggle->priv->on; + else + active = toggle->priv->off; + + return mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (toggle), active); +} + +static MateMixerSwitchOption * +mate_mixer_toggle_get_option (MateMixerSwitch *swtch, const gchar *name) +{ + MateMixerToggle *toggle; + + g_return_val_if_fail (MATE_MIXER_IS_TOGGLE (swtch), NULL); + + toggle = MATE_MIXER_TOGGLE (swtch); + + if (strcmp (name, mate_mixer_switch_option_get_name (toggle->priv->on)) == 0) + return toggle->priv->on; + if (strcmp (name, mate_mixer_switch_option_get_name (toggle->priv->off)) == 0) + return toggle->priv->off; + + return NULL; +} + +static GList * +mate_mixer_toggle_list_options (MateMixerSwitch *swtch) +{ + GList *list = NULL; + + g_return_val_if_fail (MATE_MIXER_IS_TOGGLE (swtch), NULL); + + list = g_list_prepend (list, MATE_MIXER_TOGGLE (swtch)->priv->off); + list = g_list_prepend (list, MATE_MIXER_TOGGLE (swtch)->priv->on); + + return list; +} diff --git a/libmatemixer/matemixer-toggle.h b/libmatemixer/matemixer-toggle.h new file mode 100644 index 0000000..1cedfc5 --- /dev/null +++ b/libmatemixer/matemixer-toggle.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_TOGGLE_H +#define MATEMIXER_TOGGLE_H + +#include +#include + +#include + +G_BEGIN_DECLS + +#define MATE_MIXER_TYPE_TOGGLE \ + (mate_mixer_toggle_get_type ()) +#define MATE_MIXER_TOGGLE(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_TOGGLE, MateMixerToggle)) +#define MATE_MIXER_IS_TOGGLE(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_TOGGLE)) +#define MATE_MIXER_TOGGLE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_TOGGLE, MateMixerToggleClass)) +#define MATE_MIXER_IS_TOGGLE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_TOGGLE)) +#define MATE_MIXER_TOGGLE_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_TOGGLE, MateMixerToggleClass)) + +typedef struct _MateMixerToggleClass MateMixerToggleClass; +typedef struct _MateMixerTogglePrivate MateMixerTogglePrivate; + +struct _MateMixerToggle +{ + MateMixerSwitch object; + + /*< private >*/ + MateMixerTogglePrivate *priv; +}; + +struct _MateMixerToggleClass +{ + MateMixerSwitchClass parent_class; +}; + +GType mate_mixer_toggle_get_type (void) G_GNUC_CONST; + +gboolean mate_mixer_toggle_get_state (MateMixerToggle *toggle); +gboolean mate_mixer_toggle_set_state (MateMixerToggle *toggle, + gboolean state); + +MateMixerSwitchOption *mate_mixer_toggle_get_state_option (MateMixerToggle *toggle, + gboolean state); + +G_END_DECLS + +#endif /* MATEMIXER_TOGGLE_H */ diff --git a/libmatemixer/matemixer-types.h b/libmatemixer/matemixer-types.h new file mode 100644 index 0000000..6601a95 --- /dev/null +++ b/libmatemixer/matemixer-types.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Michal Ratajsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef MATEMIXER_TYPES_H +#define MATEMIXER_TYPES_H + +G_BEGIN_DECLS + +typedef struct _MateMixerDevice MateMixerDevice; +typedef struct _MateMixerClientStream MateMixerClientStream; +typedef struct _MateMixerContext MateMixerContext; +typedef struct _MateMixerDeviceProfile MateMixerDeviceProfile; +typedef struct _MateMixerStream MateMixerStream; +typedef struct _MateMixerStreamControl MateMixerStreamControl; +typedef struct _MateMixerSwitch MateMixerSwitch; +typedef struct _MateMixerSwitchOption MateMixerSwitchOption; +typedef struct _MateMixerToggle MateMixerToggle; + +G_END_DECLS + +#endif /* MATEMIXER_TYPES_H */ diff --git a/libmatemixer/matemixer.c b/libmatemixer/matemixer.c index 0ca09b9..fa83e3f 100644 --- a/libmatemixer/matemixer.c +++ b/libmatemixer/matemixer.c @@ -54,6 +54,10 @@ mate_mixer_init (void) if (initialized == TRUE) return TRUE; +#if !GLIB_CHECK_VERSION (2, 36, 0) + g_type_init (); +#endif + load_modules (); if (modules != NULL) { @@ -96,33 +100,25 @@ mate_mixer_is_initialized (void) return initialized; } -/** - * mate_mixer_deinit: - * - * Deinitializes the library. You should call this function when you are done - * using the library. - */ -void -mate_mixer_deinit (void) +/* Return a list of loaded backend modules */ +const GList * +_mate_mixer_get_modules (void) { - GList *list; - - if (initialized == FALSE) - return; - - list = modules; - while (list != NULL) { - g_type_module_unuse (G_TYPE_MODULE (list->data)); - list = list->next; - } - initialized = FALSE; + return (const GList *) modules; } -/* Internal function: return a list of loaded backend modules */ -const GList * -mate_mixer_get_modules (void) +guint32 +_mate_mixer_create_channel_mask (MateMixerChannelPosition *positions, guint n) { - return (const GList *) modules; + guint32 mask = 0; + guint i = 0; + + for (i = 0; i < n; i++) { + if (positions[i] > MATE_MIXER_CHANNEL_UNKNOWN && + positions[i] < MATE_MIXER_CHANNEL_MAX) + mask |= 1 << positions[i]; + } + return mask; } static void @@ -150,7 +146,8 @@ load_modules (void) continue; file = g_build_filename (LIBMATEMIXER_BACKEND_DIR, name, NULL); - modules = g_list_prepend (modules, mate_mixer_backend_module_new (file)); + modules = g_list_prepend (modules, + mate_mixer_backend_module_new (file)); g_free (file); } diff --git a/libmatemixer/matemixer.h b/libmatemixer/matemixer.h index 36a3d39..a6eac79 100644 --- a/libmatemixer/matemixer.h +++ b/libmatemixer/matemixer.h @@ -22,19 +22,21 @@ #include #include -#include +#include #include #include #include -#include #include +#include +#include +#include +#include #include G_BEGIN_DECLS gboolean mate_mixer_init (void); gboolean mate_mixer_is_initialized (void); -void mate_mixer_deinit (void); G_END_DECLS -- cgit v1.2.1