diff options
Diffstat (limited to 'backends')
38 files changed, 5450 insertions, 2295 deletions
| diff --git a/backends/Makefile.am b/backends/Makefile.am index 0cf8dd4..4a8afff 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -8,12 +8,12 @@ if HAVE_PULSEAUDIO  SUBDIRS += pulse  endif -if HAVE_OSS -SUBDIRS += oss +if HAVE_ALSA +SUBDIRS += alsa  endif -if HAVE_OSS4 -SUBDIRS += oss4 +if HAVE_OSS +SUBDIRS += oss  endif  -include $(top_srcdir)/git.mk diff --git a/backends/alsa/Makefile.am b/backends/alsa/Makefile.am new file mode 100644 index 0000000..220bb3b --- /dev/null +++ b/backends/alsa/Makefile.am @@ -0,0 +1,47 @@ +backenddir = $(libdir)/libmatemixer + +backend_LTLIBRARIES = libmatemixer-alsa.la + +AM_CPPFLAGS =                                                   \ +        -I$(top_srcdir)                                         \ +        -DG_LOG_DOMAIN=\"libmatemixer-alsa\" + +libmatemixer_alsa_la_CFLAGS =                                   \ +        $(GLIB_CFLAGS)                                          \ +        $(ALSA_CFLAGS) + +libmatemixer_alsa_la_SOURCES =                                  \ +        alsa-backend.c                                          \ +        alsa-backend.h                                          \ +        alsa-constants.c                                        \ +        alsa-constants.h                                        \ +        alsa-device.c                                           \ +        alsa-device.h                                           \ +        alsa-element.c                                          \ +        alsa-element.h                                          \ +        alsa-stream.c                                           \ +        alsa-stream.h                                           \ +        alsa-stream-control.c                                   \ +        alsa-stream-control.h                                   \ +        alsa-stream-input-control.c                             \ +        alsa-stream-input-control.h                             \ +        alsa-stream-output-control.c                            \ +        alsa-stream-output-control.h                            \ +        alsa-switch.c                                           \ +        alsa-switch.h                                           \ +        alsa-switch-option.c                                    \ +        alsa-switch-option.h                                    \ +        alsa-toggle.c                                           \ +        alsa-toggle.h + +libmatemixer_alsa_la_LIBADD =                                   \ +        $(GLIB_LIBS)                                            \ +        $(ALSA_LIBS) + +libmatemixer_alsa_la_LDFLAGS =                                  \ +        -avoid-version                                          \ +        -no-undefined                                           \ +        -export-dynamic                                         \ +        -module + +-include $(top_srcdir)/git.mk diff --git a/backends/alsa/alsa-backend.c b/backends/alsa/alsa-backend.c new file mode 100644 index 0000000..7a17b85 --- /dev/null +++ b/backends/alsa/alsa-backend.c @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "alsa-backend.h" +#include "alsa-device.h" +#include "alsa-stream.h" + +#define BACKEND_NAME      "ALSA" +#define BACKEND_PRIORITY  9 + +struct _AlsaBackendPrivate +{ +    GSource    *timeout_source; +    GHashTable *devices; +    GHashTable *devices_ids; +}; + +static void alsa_backend_class_init     (AlsaBackendClass *klass); +static void alsa_backend_class_finalize (AlsaBackendClass *klass); +static void alsa_backend_init           (AlsaBackend      *alsa); +static void alsa_backend_dispose        (GObject          *object); +static void alsa_backend_finalize       (GObject          *object); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_DYNAMIC_TYPE (AlsaBackend, alsa_backend, MATE_MIXER_TYPE_BACKEND) +#pragma clang diagnostic pop + +static gboolean alsa_backend_open            (MateMixerBackend *backend); +static void     alsa_backend_close           (MateMixerBackend *backend); +static GList *  alsa_backend_list_devices    (MateMixerBackend *backend); +static GList *  alsa_backend_list_streams    (MateMixerBackend *backend); + +static gboolean read_devices                 (AlsaBackend      *alsa); + +static gboolean read_device                  (AlsaBackend      *alsa, +                                              const gchar      *card); + +static void     add_device                   (AlsaBackend      *alsa, +                                              AlsaDevice       *device); + +static void     remove_device                (AlsaBackend      *alsa, +                                              AlsaDevice       *device); +static void     remove_stream                (AlsaBackend      *alsa, +                                              const gchar      *name); + +static void     select_default_input_stream  (AlsaBackend      *alsa); +static void     select_default_output_stream (AlsaBackend      *alsa); + +static MateMixerBackendInfo info; + +void +backend_module_init (GTypeModule *module) +{ +    alsa_backend_register_type (module); + +    info.name         = BACKEND_NAME; +    info.priority     = BACKEND_PRIORITY; +    info.g_type       = ALSA_TYPE_BACKEND; +    info.backend_type = MATE_MIXER_BACKEND_ALSA; +} + +const MateMixerBackendInfo *backend_module_get_info (void) +{ +    return &info; +} + +static void +alsa_backend_class_init (AlsaBackendClass *klass) +{ +    GObjectClass          *object_class; +    MateMixerBackendClass *backend_class; + +    object_class = G_OBJECT_CLASS (klass); +    object_class->dispose  = alsa_backend_dispose; +    object_class->finalize = alsa_backend_finalize; + +    backend_class = MATE_MIXER_BACKEND_CLASS (klass); +    backend_class->open         = alsa_backend_open; +    backend_class->close        = alsa_backend_close; +    backend_class->list_devices = alsa_backend_list_devices; +    backend_class->list_streams = alsa_backend_list_streams; + +    g_type_class_add_private (object_class, sizeof (AlsaBackendPrivate)); +} + +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE() */ +static void +alsa_backend_class_finalize (AlsaBackendClass *klass) +{ +} + +static void +alsa_backend_init (AlsaBackend *alsa) +{ +    alsa->priv = G_TYPE_INSTANCE_GET_PRIVATE (alsa, +                                             ALSA_TYPE_BACKEND, +                                             AlsaBackendPrivate); + +    alsa->priv->devices = g_hash_table_new_full (g_str_hash, +                                                 g_str_equal, +                                                 g_free, +                                                 g_object_unref); + +    alsa->priv->devices_ids = g_hash_table_new_full (g_str_hash, +                                                     g_str_equal, +                                                     g_free, +                                                     NULL); +} + +static void +alsa_backend_dispose (GObject *object) +{ +    MateMixerBackend *backend; +    MateMixerState    state; + +    backend = MATE_MIXER_BACKEND (object); + +    state = mate_mixer_backend_get_state (backend); +    if (state != MATE_MIXER_STATE_IDLE) +        alsa_backend_close (backend); + +    G_OBJECT_CLASS (alsa_backend_parent_class)->dispose (object); +} + +static void +alsa_backend_finalize (GObject *object) +{ +    AlsaBackend *alsa; + +    alsa = ALSA_BACKEND (object); + +    g_hash_table_unref (alsa->priv->devices); +    g_hash_table_unref (alsa->priv->devices_ids); + +    G_OBJECT_CLASS (alsa_backend_parent_class)->finalize (object); +} + +static gboolean +alsa_backend_open (MateMixerBackend *backend) +{ +    AlsaBackend *alsa; + +    g_return_val_if_fail (ALSA_IS_BACKEND (backend), FALSE); + +    alsa = ALSA_BACKEND (backend); + +    /* Poll ALSA for changes every 500 milliseconds, this actually only +     * discovers added or changed sound cards, sound card related events +     * are handled by AlsaDevices */ +    alsa->priv->timeout_source = g_timeout_source_new_seconds (1); +    g_source_set_callback (alsa->priv->timeout_source, +                           (GSourceFunc) read_devices, +                           alsa, +                           NULL); +    g_source_attach (alsa->priv->timeout_source, +                     g_main_context_get_thread_default ()); + +    /* Read the initial list of devices so we have some starting point, there +     * isn't really a way to detect errors here, failing to add a device may +     * be a device-related problem so make the backend always open successfully */ +    read_devices (alsa); + +    _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_READY); +    return TRUE; +} + +void +alsa_backend_close (MateMixerBackend *backend) +{ +    AlsaBackend *alsa; + +    g_return_if_fail (ALSA_IS_BACKEND (backend)); + +    alsa = ALSA_BACKEND (backend); + +    g_source_destroy (alsa->priv->timeout_source); + +    g_hash_table_remove_all (alsa->priv->devices); +    g_hash_table_remove_all (alsa->priv->devices_ids); + +    _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE); +} + +static GList * +alsa_backend_list_devices (MateMixerBackend *backend) +{ +    GList *list; + +    g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL); + +    /* Convert the hash table to a linked list, this list is expected to be +     * cached in the main library */ +    list = g_hash_table_get_values (ALSA_BACKEND (backend)->priv->devices); +    if (list != NULL) +        g_list_foreach (list, (GFunc) g_object_ref, NULL); + +    return list; +} + +static GList * +alsa_backend_list_streams (MateMixerBackend *backend) +{ +    AlsaBackend   *alsa; +    GHashTableIter iter; +    gpointer       value; +    GList         *list = NULL; + +    g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL); + +    alsa = ALSA_BACKEND (backend); + +    /* We don't keep a list or hash table of all streams here, instead walk +     * through the list of devices and create the list manually, each device +     * has at most one input and one output stream */ +    g_hash_table_iter_init (&iter, alsa->priv->devices); + +    while (g_hash_table_iter_next (&iter, NULL, &value)) { +        AlsaDevice *device = ALSA_DEVICE (value); +        AlsaStream *stream; + +        stream = alsa_device_get_output_stream (device); +        if (stream != NULL) +            list = g_list_prepend (list, stream); +        stream = alsa_device_get_input_stream (device); +        if (stream != NULL) +            list = g_list_prepend (list, stream); +    } + +    if (list != NULL) +        g_list_foreach (list, (GFunc) g_object_ref, NULL); + +    return list; +} + +static gboolean +read_devices (AlsaBackend *alsa) +{ +    gint     num; +    gint     ret; +    gchar    card[16]; +    gboolean changed = FALSE; + +    /* Read the default device first, it will be either one of the hardware cards +     * that will be queried later, or a software mixer */ +    if (read_device (alsa, "default") == TRUE) +        changed = TRUE; + +    for (num = -1;;) { +        /* Read number of the next sound card */ +        ret = snd_card_next (&num); +        if (ret < 0 || +            num < 0) +            break; + +        g_snprintf (card, sizeof (card), "hw:%d", num); + +        if (read_device (alsa, card) == TRUE) +            changed = TRUE; +    } + +    /* If any card has been added, make sure we have the most suitable default +     * input and output streams */ +    if (changed == TRUE) { +        select_default_input_stream (alsa); +        select_default_output_stream (alsa); +    } +    return G_SOURCE_CONTINUE; +} + +static gboolean +read_device (AlsaBackend *alsa, const gchar *card) +{ +    AlsaDevice          *device; +    snd_ctl_t           *ctl; +    snd_ctl_card_info_t *info; +    const gchar         *id; +    gint                 ret; + +    /* The device may be already known, remove it if it's known and fails +     * to be read, this happens for example when PulseAudio is killed */ +    device = g_hash_table_lookup (alsa->priv->devices, card); + +    ret = snd_ctl_open (&ctl, card, 0); +    if (ret < 0) { +        g_warning ("Failed to open ALSA control for %s: %s", +                   card, +                   snd_strerror (ret)); +        if (device != NULL) +            remove_device (alsa, device); +        return FALSE; +    } + +    snd_ctl_card_info_alloca (&info); + +    ret = snd_ctl_card_info (ctl, info); +    if (ret < 0) { +        g_warning ("Failed to read card info: %s", snd_strerror (ret)); +        if (device != NULL) +            remove_device (alsa, device); + +        snd_ctl_close (ctl); +        return FALSE; +    } + +    id = snd_ctl_card_info_get_id (info); + +    /* We also keep a list of device identifiers to be sure no card is +     * added twice, this could commonly happen because some card may +     * also be assigned to the "default" ALSA device */ +    if (g_hash_table_contains (alsa->priv->devices_ids, id) == TRUE) { +        snd_ctl_close (ctl); +        return FALSE; +    } + +    device = alsa_device_new (card, snd_ctl_card_info_get_name (info)); + +    if (alsa_device_open (device) == FALSE) { +        g_object_unref (device); +        snd_ctl_close (ctl); +        return FALSE; +    } + +    g_object_set_data_full (G_OBJECT (device), +                            "__matemixer_alsa_device_id", +                            g_strdup (id), +                            g_free); + +    add_device (alsa, device); + +    snd_ctl_close (ctl); +    return TRUE; +} + +static void +add_device (AlsaBackend *alsa, AlsaDevice *device) +{ +    const gchar *name; + +    name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + +    g_hash_table_insert (alsa->priv->devices, +                         g_strdup (name), +                         g_object_ref (device)); + +    /* Remember the device identifier, use a single string copy as we only free +     * the hash table key */ +    g_hash_table_add (alsa->priv->devices_ids, +                      g_strdup (g_object_get_data (G_OBJECT (device), +                                                   "__matemixer_alsa_device_id"))); + +    g_signal_connect_swapped (G_OBJECT (device), +                              "closed", +                              G_CALLBACK (remove_device), +                              alsa); +    g_signal_connect_swapped (G_OBJECT (device), +                              "stream-removed", +                              G_CALLBACK (remove_stream), +                              alsa); + +    g_signal_emit_by_name (G_OBJECT (alsa), "device-added", name); + +    /* Load the device elements after emitting device-added, because the load +     * function will most likely emit stream-added on the device and backend */ +    alsa_device_load (device); +} + +static void +remove_device (AlsaBackend *alsa, AlsaDevice *device) +{ +    const gchar *name; + +    name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + +    g_signal_handlers_disconnect_by_func (G_OBJECT (device), +                                          G_CALLBACK (remove_device), +                                          alsa); +    g_signal_handlers_disconnect_by_func (G_OBJECT (device), +                                          G_CALLBACK (remove_stream), +                                          alsa); + +    /* Remove the device */ +    g_hash_table_remove (alsa->priv->devices_ids, +                         g_object_get_data (G_OBJECT (device), +                                            "__matemixer_alsa_device_id")); + +    // XXX close the device and make it remove streams +    g_hash_table_remove (alsa->priv->devices, name); +    g_signal_emit_by_name (G_OBJECT (alsa), +                           "device-removed", +                           name); +} + +static void +remove_stream (AlsaBackend *alsa, const gchar *name) +{ +    MateMixerStream *stream; + +    stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (alsa)); + +    // XXX see if the change happens after stream is removed or before +    if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) +        select_default_input_stream (alsa); + +    stream = mate_mixer_backend_get_default_output_stream (MATE_MIXER_BACKEND (alsa)); + +    if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) +        select_default_output_stream (alsa); +} + +static void +select_default_input_stream (AlsaBackend *alsa) +{ +    AlsaDevice *device; +    AlsaStream *stream; +    gchar       card[16]; +    gint        num; + +    /* Always prefer stream in the "default" device */ +    device = g_hash_table_lookup (alsa->priv->devices, "default"); +    if (device != NULL) { +        stream = alsa_device_get_input_stream (device); +        if (stream != NULL) { +            _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa), +                                                          MATE_MIXER_STREAM (stream)); +            return; +        } +    } + +    /* If there is no input stream in the default device, search the cards in +     * the correct order */ +    for (num = 0;; num++) { +        g_snprintf (card, sizeof (card), "hw:%d", num); + +        device = g_hash_table_lookup (alsa->priv->devices, card); +        if (device == NULL) +            break; +        stream = alsa_device_get_input_stream (device); +        if (stream != NULL) { +            _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa), +                                                          MATE_MIXER_STREAM (stream)); +            return; +        } +    } + +    /* In the worst case unset the default stream */ +    _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa), NULL); +} + +static void +select_default_output_stream (AlsaBackend *alsa) +{ +    AlsaDevice *device; +    AlsaStream *stream; +    gchar       card[16]; +    gint        num; + +    /* Always prefer stream in the "default" device */ +    device = g_hash_table_lookup (alsa->priv->devices, "default"); +    if (device != NULL) { +        stream = alsa_device_get_output_stream (device); +        if (stream != NULL) { +            _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa), +                                                           MATE_MIXER_STREAM (stream)); +            return; +        } +    } + +    /* If there is no input stream in the default device, search the cards in +     * the correct order */ +    for (num = 0;; num++) { +        g_snprintf (card, sizeof (card), "hw:%d", num); + +        device = g_hash_table_lookup (alsa->priv->devices, card); +        if (device == NULL) +            break; +        stream = alsa_device_get_output_stream (device); +        if (stream != NULL) { +            _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa), +                                                           MATE_MIXER_STREAM (stream)); +            return; +        } +    } + +    /* In the worst case unset the default stream */ +    _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa), NULL); +} diff --git a/backends/alsa/alsa-backend.h b/backends/alsa/alsa-backend.h new file mode 100644 index 0000000..03fedf0 --- /dev/null +++ b/backends/alsa/alsa-backend.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_BACKEND_H +#define ALSA_BACKEND_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#define ALSA_TYPE_BACKEND                       \ +        (alsa_backend_get_type ()) +#define ALSA_BACKEND(o)                         \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_BACKEND, AlsaBackend)) +#define ALSA_IS_BACKEND(o)                      \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_BACKEND)) +#define ALSA_BACKEND_CLASS(k)                   \ +        (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_BACKEND, AlsaBackendClass)) +#define ALSA_IS_BACKEND_CLASS(k)                \ +        (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_BACKEND)) +#define ALSA_BACKEND_GET_CLASS(o)               \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_BACKEND, AlsaBackendClass)) + +typedef struct _AlsaBackend         AlsaBackend; +typedef struct _AlsaBackendClass    AlsaBackendClass; +typedef struct _AlsaBackendPrivate  AlsaBackendPrivate; + +struct _AlsaBackend +{ +    MateMixerBackend parent; + +    /*< private >*/ +    AlsaBackendPrivate *priv; +}; + +struct _AlsaBackendClass +{ +    MateMixerBackendClass parent_class; +}; + +GType                       alsa_backend_get_type   (void) G_GNUC_CONST; + +/* Support function for dynamic loading of the backend module */ +void                        backend_module_init     (GTypeModule *module); +const MateMixerBackendInfo *backend_module_get_info (void); + +#endif /* ALSA_BACKEND_H */ diff --git a/backends/alsa/alsa-constants.c b/backends/alsa/alsa-constants.c new file mode 100644 index 0000000..2124a2e --- /dev/null +++ b/backends/alsa/alsa-constants.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib/gi18n.h> +#include <alsa/asoundlib.h> +#include <libmatemixer/matemixer.h> + +#include "alsa-constants.h" + +// XXX add more and probably move them somewhere else +const AlsaControlInfo alsa_controls[] = +{ +    { "Master",   N_("Master"),  MATE_MIXER_STREAM_CONTROL_ROLE_MASTER }, +    { "Speaker",  N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER }, +    { "Capture",  N_("Capture"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER }, +    { "PCM",      N_("PCM"),     MATE_MIXER_STREAM_CONTROL_ROLE_PCM }, +    { "Line",     N_("Line"),    MATE_MIXER_STREAM_CONTROL_ROLE_PORT }, +    { "Mic",      N_("Mic"),     MATE_MIXER_STREAM_CONTROL_ROLE_PORT }, +    { NULL } +}; + +const MateMixerChannelPosition alsa_channel_map_from[SND_MIXER_SCHN_LAST] = +{ +    [SND_MIXER_SCHN_FRONT_LEFT]             = MATE_MIXER_CHANNEL_FRONT_LEFT, +    [SND_MIXER_SCHN_FRONT_RIGHT]            = MATE_MIXER_CHANNEL_FRONT_RIGHT, +    [SND_MIXER_SCHN_REAR_LEFT]              = MATE_MIXER_CHANNEL_BACK_LEFT, +    [SND_MIXER_SCHN_REAR_RIGHT]             = MATE_MIXER_CHANNEL_BACK_RIGHT, +    [SND_MIXER_SCHN_FRONT_CENTER]           = MATE_MIXER_CHANNEL_FRONT_CENTER, +    [SND_MIXER_SCHN_WOOFER]                 = MATE_MIXER_CHANNEL_LFE, +    [SND_MIXER_SCHN_SIDE_LEFT]              = MATE_MIXER_CHANNEL_SIDE_LEFT, +    [SND_MIXER_SCHN_SIDE_RIGHT]             = MATE_MIXER_CHANNEL_SIDE_RIGHT, +    [SND_MIXER_SCHN_REAR_CENTER]            = MATE_MIXER_CHANNEL_BACK_CENTER +}; + +const snd_mixer_selem_channel_id_t alsa_channel_map_to[MATE_MIXER_CHANNEL_MAX] = +{ +    [MATE_MIXER_CHANNEL_UNKNOWN]            = SND_MIXER_SCHN_UNKNOWN, +    [MATE_MIXER_CHANNEL_MONO]               = SND_MIXER_SCHN_MONO, +    [MATE_MIXER_CHANNEL_FRONT_LEFT]         = SND_MIXER_SCHN_FRONT_LEFT, +    [MATE_MIXER_CHANNEL_FRONT_RIGHT]        = SND_MIXER_SCHN_FRONT_RIGHT, +    [MATE_MIXER_CHANNEL_FRONT_CENTER]       = SND_MIXER_SCHN_FRONT_CENTER, +    [MATE_MIXER_CHANNEL_LFE]                = SND_MIXER_SCHN_WOOFER, +    [MATE_MIXER_CHANNEL_BACK_LEFT]          = SND_MIXER_SCHN_REAR_LEFT, +    [MATE_MIXER_CHANNEL_BACK_RIGHT]         = SND_MIXER_SCHN_REAR_RIGHT, +    [MATE_MIXER_CHANNEL_BACK_CENTER]        = SND_MIXER_SCHN_REAR_CENTER, +    [MATE_MIXER_CHANNEL_FRONT_LEFT_CENTER]  = SND_MIXER_SCHN_UNKNOWN, +    [MATE_MIXER_CHANNEL_FRONT_RIGHT_CENTER] = SND_MIXER_SCHN_UNKNOWN, +    [MATE_MIXER_CHANNEL_SIDE_LEFT]          = SND_MIXER_SCHN_SIDE_LEFT, +    [MATE_MIXER_CHANNEL_SIDE_RIGHT]         = SND_MIXER_SCHN_SIDE_RIGHT, +    [MATE_MIXER_CHANNEL_TOP_FRONT_LEFT]     = SND_MIXER_SCHN_UNKNOWN, +    [MATE_MIXER_CHANNEL_TOP_FRONT_RIGHT]    = SND_MIXER_SCHN_UNKNOWN, +    [MATE_MIXER_CHANNEL_TOP_FRONT_CENTER]   = SND_MIXER_SCHN_UNKNOWN, +    [MATE_MIXER_CHANNEL_TOP_CENTER]         = SND_MIXER_SCHN_UNKNOWN, +    [MATE_MIXER_CHANNEL_TOP_BACK_LEFT]      = SND_MIXER_SCHN_UNKNOWN, +    [MATE_MIXER_CHANNEL_TOP_BACK_RIGHT]     = SND_MIXER_SCHN_UNKNOWN, +    [MATE_MIXER_CHANNEL_TOP_BACK_CENTER]    = SND_MIXER_SCHN_UNKNOWN +}; diff --git a/backends/oss4/oss4-common.h b/backends/alsa/alsa-constants.h index fe55b2b..81257c7 100644 --- a/backends/oss4/oss4-common.h +++ b/backends/alsa/alsa-constants.h @@ -15,24 +15,21 @@   * License along with this library; if not, see <http://www.gnu.org/licenses/>.   */ -#ifndef OSS4_COMMON_H -#define OSS4_COMMON_H +#ifndef ALSA_CONSTANTS_H +#define ALSA_CONSTANTS_H -#include "config.h" +#include <glib.h> +#include <alsa/asoundlib.h> +#include <libmatemixer/matemixer.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <fcntl.h> +typedef struct { +    gchar                     *name; +    gchar                     *label; +    MateMixerStreamControlRole role; +} AlsaControlInfo; -#ifdef HAVE_SYS_SOUNDCARD_H -#  include <sys/soundcard.h> -#elif HAVE_SOUNDCARD_H -#  include <soundcard.h> -#elif HAVE_MACHINE_SOUNDCARD_H -#  include <machine/soundcard.h> -#else -#  error "No OSS4 header file present" -#endif +extern const AlsaControlInfo                alsa_controls[]; +extern const MateMixerChannelPosition       alsa_channel_map_from[]; +extern const snd_mixer_selem_channel_id_t   alsa_channel_map_to[]; -#endif /* OSS4_COMMON_H */ +#endif /* ALSA_CONSTANTS_H */ diff --git a/backends/alsa/alsa-device.c b/backends/alsa/alsa-device.c new file mode 100644 index 0000000..5acc6f5 --- /dev/null +++ b/backends/alsa/alsa-device.c @@ -0,0 +1,941 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <strings.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> +#include <libmatemixer/matemixer.h> + +#include "alsa-constants.h" +#include "alsa-device.h" +#include "alsa-element.h" +#include "alsa-stream.h" +#include "alsa-stream-control.h" +#include "alsa-stream-input-control.h" +#include "alsa-stream-output-control.h" +#include "alsa-switch.h" +#include "alsa-switch-option.h" +#include "alsa-toggle.h" + +#define ALSA_DEVICE_ICON "audio-card" + +struct _AlsaDevicePrivate +{ +    snd_mixer_t  *handle; +    GMainContext *context; +    GMutex        mutex; +    GCond         cond; +    AlsaStream   *input; +    AlsaStream   *output; +    GHashTable   *switches; +    gboolean      events_pending; +}; + +enum { +    CLOSED, +    N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0, }; + +static void alsa_device_class_init (AlsaDeviceClass *klass); +static void alsa_device_init       (AlsaDevice      *device); +static void alsa_device_dispose    (GObject         *object); +static void alsa_device_finalize   (GObject         *object); + +G_DEFINE_TYPE (AlsaDevice, alsa_device, MATE_MIXER_TYPE_DEVICE) + +static MateMixerSwitch *alsa_device_get_switch    (MateMixerDevice            *mmd, +                                                   const gchar                *name); + +static GList *          alsa_device_list_streams  (MateMixerDevice            *mmd); +static GList *          alsa_device_list_switches (MateMixerDevice            *mmd); + +static gboolean         add_stream_input_control  (AlsaDevice                 *device, +                                                   snd_mixer_elem_t           *el); +static gboolean         add_stream_output_control (AlsaDevice                 *device, +                                                   snd_mixer_elem_t           *el); + +static gboolean         add_switch                (AlsaDevice                 *device, +                                                   AlsaStream                 *stream, +                                                   snd_mixer_elem_t           *el); + +static gboolean         add_device_switch         (AlsaDevice                 *device, +                                                   snd_mixer_elem_t           *el); + +static gboolean         add_stream_input_switch   (AlsaDevice                 *device, +                                                   snd_mixer_elem_t           *el); +static gboolean         add_stream_output_switch  (AlsaDevice                 *device, +                                                   snd_mixer_elem_t           *el); + +static gboolean         add_stream_input_toggle   (AlsaDevice                 *device, +                                                   snd_mixer_elem_t           *el); +static gboolean         add_stream_output_toggle  (AlsaDevice                 *device, +                                                   snd_mixer_elem_t           *el); + +static void             load_element              (AlsaDevice                 *device, +                                                   snd_mixer_elem_t           *el); + +static void             load_elements_by_name     (AlsaDevice                 *device, +                                                   const gchar                *name); + +static void             remove_elements_by_name   (AlsaDevice                 *device, +                                                   const gchar                *name); + +static void             handle_poll               (AlsaDevice                 *device); + +static gboolean         handle_process_events     (AlsaDevice                 *device); + +static int              handle_callback           (snd_mixer_t                *handle, +                                                   guint                       mask, +                                                   snd_mixer_elem_t           *el); +static int              handle_element_callback   (snd_mixer_elem_t           *el, +                                                   guint                       mask); + +static void             close_device              (AlsaDevice                 *device); + +static gchar *          get_element_name          (snd_mixer_elem_t           *el); +static void             get_control_info          (snd_mixer_elem_t           *el, +                                                   gchar                     **name, +                                                   gchar                     **label, +                                                   MateMixerStreamControlRole *role); + +static void             get_switch_info           (snd_mixer_elem_t           *el, +                                                   gchar                     **name, +                                                   gchar                     **label); + +static void +alsa_device_class_init (AlsaDeviceClass *klass) +{ +    GObjectClass         *object_class; +    MateMixerDeviceClass *device_class; + +    object_class = G_OBJECT_CLASS (klass); +    object_class->dispose  = alsa_device_dispose; +    object_class->finalize = alsa_device_finalize; + +    device_class = MATE_MIXER_DEVICE_CLASS (klass); +    device_class->get_switch    = alsa_device_get_switch; +    device_class->list_streams  = alsa_device_list_streams; +    device_class->list_switches = alsa_device_list_switches; + +    signals[CLOSED] = +        g_signal_new ("closed", +                      G_TYPE_FROM_CLASS (object_class), +                      G_SIGNAL_RUN_LAST, +                      G_STRUCT_OFFSET (AlsaDeviceClass, closed), +                      NULL, +                      NULL, +                      g_cclosure_marshal_VOID__VOID, +                      G_TYPE_NONE, +                      0, +                      G_TYPE_NONE); + +    g_type_class_add_private (object_class, sizeof (AlsaDevicePrivate)); +} + +static void +alsa_device_init (AlsaDevice *device) +{ +    device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, +                                                ALSA_TYPE_DEVICE, +                                                AlsaDevicePrivate); + +    device->priv->switches = g_hash_table_new_full (g_str_hash, +                                                    g_str_equal, +                                                    g_free, +                                                    g_object_unref); + +    device->priv->context = g_main_context_ref_thread_default (); + +    g_mutex_init (&device->priv->mutex); +    g_cond_init (&device->priv->cond); +} + +static void +alsa_device_dispose (GObject *object) +{ +    AlsaDevice *device; + +    device = ALSA_DEVICE (object); + +    g_clear_object (&device->priv->input); +    g_clear_object (&device->priv->output); + +    g_hash_table_remove_all (device->priv->switches); + +    G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object); +} + +static void +alsa_device_finalize (GObject *object) +{ +    AlsaDevice *device; + +    device = ALSA_DEVICE (object); + +    g_mutex_clear (&device->priv->mutex); +    g_cond_clear (&device->priv->cond); + +    g_hash_table_unref (device->priv->switches); +    g_main_context_unref (device->priv->context); + +    if (device->priv->handle != NULL) +        snd_mixer_close (device->priv->handle); + +    G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object); +} + +AlsaDevice * +alsa_device_new (const gchar *name, const gchar *label) +{ +    AlsaDevice  *device; +    gchar       *stream_name; + +    g_return_val_if_fail (name != NULL, NULL); +    g_return_val_if_fail (label != NULL, NULL); + +    device = g_object_new (ALSA_TYPE_DEVICE, +                           "name", name, +                           "label", label, +                           "icon", ALSA_DEVICE_ICON, +                           NULL); + +    /* Create input and output streams, they will exist the whole time, but +     * the added and removed signals will be emitted when the first control or +     * switch is added or the last one removed */ +    stream_name = g_strdup_printf ("alsa-input-%s", name); +    device->priv->input = alsa_stream_new (stream_name, +                                           MATE_MIXER_DEVICE (device), +                                           MATE_MIXER_STREAM_INPUT); +    g_free (stream_name); + +    stream_name = g_strdup_printf ("alsa-output-%s", name); +    device->priv->output = alsa_stream_new (stream_name, +                                            MATE_MIXER_DEVICE (device), +                                            MATE_MIXER_STREAM_OUTPUT); +    g_free (stream_name); + +    return device; +} + +gboolean +alsa_device_open (AlsaDevice *device) +{ +    snd_mixer_t *handle; +    const gchar *name; +    gint         ret; + +    g_return_val_if_fail (ALSA_IS_DEVICE (device), FALSE); +    g_return_val_if_fail (device->priv->handle == NULL, FALSE); + +    name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + +    g_debug ("Opening device %s (%s)", +             name, +             mate_mixer_device_get_label (MATE_MIXER_DEVICE (device))); + +    /* Open the mixer for the current device */ +    ret = snd_mixer_open (&handle, 0); +    if (ret < 0) { +        g_warning ("Failed to open mixer: %s", snd_strerror (ret)); +        return FALSE; +    } +    ret = snd_mixer_attach (handle, name); +    if (ret < 0) { +        g_warning ("Failed to attach mixer to %s: %s", +                   name, +                   snd_strerror (ret)); + +        snd_mixer_close (handle); +        return FALSE; +    } +    ret = snd_mixer_selem_register (handle, NULL, NULL); +    if (ret < 0) { +        g_warning ("Failed to register simple element for %s: %s", +                   name, +                   snd_strerror (ret)); + +        snd_mixer_close (handle); +        return FALSE; +    } +    ret = snd_mixer_load (handle); +    if (ret < 0) { +        g_warning ("Failed to load mixer elements for %s: %s", +                   name, +                   snd_strerror (ret)); + +        snd_mixer_close (handle); +        return FALSE; +    } + +    device->priv->handle = handle; +    return TRUE; +} + +void +alsa_device_load (AlsaDevice *device) +{ +    GThread          *thread; +    GError           *error = NULL; +    snd_mixer_elem_t *el; + +    g_return_if_fail (ALSA_IS_DEVICE (device)); +    g_return_if_fail (device->priv->handle != NULL); + +    /* Process the mixer elements */ +    el = snd_mixer_first_elem (device->priv->handle); +    while (el != NULL) { +        load_element (device, el); + +        el = snd_mixer_elem_next (el); +    } + +    /* Set callback for ALSA events */ +    snd_mixer_set_callback (device->priv->handle, handle_callback); +    snd_mixer_set_callback_private (device->priv->handle, device); + +    /* Start the polling thread */ +    thread = g_thread_try_new ("matemixer-alsa-poll", +                               (GThreadFunc) handle_poll, +                               device, +                               &error); +    if (thread == NULL) { +        /* The error is not treated as fatal, because without the polling +         * thread we still have most of the functionality */ +        g_warning ("Failed to create poll thread: %s", error->message); +        g_error_free (error); +    } else +        g_thread_unref (thread); +} + +AlsaStream * +alsa_device_get_input_stream (AlsaDevice *device) +{ +    g_return_val_if_fail (ALSA_IS_DEVICE (device), NULL); + +    /* Normally controlless streams should not exist, here we simulate the +     * behaviour for the owning instance */ +    if (alsa_stream_is_empty (device->priv->input) == FALSE) +        return device->priv->input; + +    return NULL; +} + +AlsaStream * +alsa_device_get_output_stream (AlsaDevice *device) +{ +    g_return_val_if_fail (ALSA_IS_DEVICE (device), NULL); + +    /* Normally controlless streams should not exist, here we simulate the +     * behaviour for the owning instance */ +    if (alsa_stream_is_empty (device->priv->output) == FALSE) +        return device->priv->output; + +    return NULL; +} + +static gboolean +add_element (AlsaDevice *device, AlsaStream *stream, AlsaElement *element) +{ +    gboolean added = FALSE; + +    if (alsa_element_load (element) == FALSE) +        return FALSE; + +    if (stream != NULL) { +        gboolean empty = FALSE; + +        if (alsa_stream_is_empty (stream) == TRUE) { +            const gchar *name = +                mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); + +            /* Pretend the stream has just been created now that we are adding +             * the first control */ +            g_signal_emit_by_name (G_OBJECT (device), +                                   "stream-added", +                                   name); +            empty = TRUE; +        } + +        if (ALSA_IS_STREAM_CONTROL (element)) { +            alsa_stream_add_control (stream, ALSA_STREAM_CONTROL (element)); + +            /* If this is the first control, set it as the default one. +             * The controls often seem to come in the order of importance, but this is +             * driver specific, so we may later see if there is another control which +             * better matches the default. */ +            if (empty == TRUE) +                alsa_stream_set_default_control (stream, ALSA_STREAM_CONTROL (element)); + +            added = TRUE; +        } else if (ALSA_IS_SWITCH (element)) { +            /* Switch belonging to a stream */ +            alsa_stream_add_switch (stream, ALSA_SWITCH (element)); +            added = TRUE; +        } +    } else if (ALSA_IS_SWITCH (element)) { +        /* Switch belonging to the device */ +        const gchar *name = +            mate_mixer_switch_get_name (MATE_MIXER_SWITCH (element)); + +        g_hash_table_insert (device->priv->switches, +                             g_strdup (name), +                             g_object_ref (element)); + +        g_signal_emit_by_name (G_OBJECT (device), +                               "switch-added", +                               name); +        added = TRUE; +    } + +    if G_LIKELY (added == TRUE) { +        snd_mixer_elem_t *el = alsa_element_get_snd_element (element); + +        snd_mixer_elem_set_callback (el, handle_element_callback); +        snd_mixer_elem_set_callback_private (el, device); +    } +    return added; +} + +static MateMixerSwitch * +alsa_device_get_switch (MateMixerDevice *mmd, const gchar *name) +{ +    g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL); +    g_return_val_if_fail (name != NULL, NULL); + +    return g_hash_table_lookup (ALSA_DEVICE (mmd)->priv->switches, name); +} + +static GList * +alsa_device_list_streams (MateMixerDevice *mmd) +{ +    AlsaDevice *device; +    GList      *list = NULL; + +    g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL); + +    device = ALSA_DEVICE (mmd); + +    if (device->priv->output != NULL) +        list = g_list_prepend (list, g_object_ref (device->priv->output)); +    if (device->priv->input != NULL) +        list = g_list_prepend (list, g_object_ref (device->priv->input)); + +    return list; +} + +static GList * +alsa_device_list_switches (MateMixerDevice *mmd) +{ +    GList *list; + +    g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL); + +    /* Convert the hash table to a linked list, this list is expected to be +     * cached in the main library */ +    list = g_hash_table_get_values (ALSA_DEVICE (mmd)->priv->switches); +    if (list != NULL) +        g_list_foreach (list, (GFunc) g_object_ref, NULL); + +    return list; +} + +static gboolean +add_stream_input_control (AlsaDevice *device, snd_mixer_elem_t *el) +{ +    AlsaStreamControl         *control; +    gchar                     *name; +    gchar                     *label; +    MateMixerStreamControlRole role; + +    get_control_info (el, &name, &label, &role); + +    g_debug ("Found device %s input control %s", +             mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), +             label); + +    control = alsa_stream_input_control_new (name, label, role); +    g_free (name); +    g_free (label); + +    alsa_element_set_snd_element (ALSA_ELEMENT (control), el); + +    if (add_element (device, device->priv->input, ALSA_ELEMENT (control)) == FALSE) { +        g_object_unref (control); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +add_stream_output_control (AlsaDevice *device, snd_mixer_elem_t *el) +{ +    AlsaStreamControl         *control; +    gchar                     *label; +    gchar                     *name; +    MateMixerStreamControlRole role; + +    get_control_info (el, &name, &label, &role); + +    g_debug ("Found device %s output control %s", +             mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), +             label); + +    control = alsa_stream_output_control_new (name, label, role); +    g_free (name); +    g_free (label); + +    alsa_element_set_snd_element (ALSA_ELEMENT (control), el); + +    if (add_element (device, device->priv->output, ALSA_ELEMENT (control)) == FALSE) { +        g_object_unref (control); +        return FALSE; +    } +    return TRUE; +} + +static AlsaToggle * +create_toggle (AlsaDevice *device, snd_mixer_elem_t *el, AlsaToggleType type) +{ +    AlsaToggle       *toggle; +    AlsaSwitchOption *on; +    AlsaSwitchOption *off; +    gchar            *name; +    gchar            *label; + +    on  = alsa_switch_option_new ("On", _("On"), NULL, 1); +    off = alsa_switch_option_new ("Off", _("Off"), NULL, 0); + +    get_switch_info (el, &name, &label); +    toggle = alsa_toggle_new (name, +                              label, +                              type, +                              on, off); +    g_free (name); +    g_free (label); + +    alsa_element_set_snd_element (ALSA_ELEMENT (toggle), el); + +    g_object_unref (on); +    g_object_unref (off); + +    return toggle; +} + +static gboolean +add_switch (AlsaDevice *device, AlsaStream *stream, snd_mixer_elem_t *el) +{ +    AlsaElement *element = NULL; +    GList       *options = NULL; +    gchar       *name; +    gchar       *label; +    gchar        item[128]; +    guint        i; +    gint         count; +    gint         ret; + +    count = snd_mixer_selem_get_enum_items (el); +    if G_UNLIKELY (count <= 0) { +        g_debug ("Skipping mixer switch %s with %d items", +                 snd_mixer_selem_get_name (el), +                 count); +        return FALSE; +    } + +    for (i = 0; i < count; i++) { +        ret = snd_mixer_selem_get_enum_item_name (el, i, +                                                  sizeof (item), +                                                  item); +        if G_LIKELY (ret == 0) +            options = g_list_prepend (options, +                                      alsa_switch_option_new (item, item, NULL, i)); +        else +            g_warning ("Failed to read switch item name: %s", snd_strerror (ret)); +    } + +    get_switch_info (el, &name, &label); + +    /* Takes ownership of options */ +    element = ALSA_ELEMENT (alsa_switch_new (name, label, g_list_reverse (options))); +    g_free (name); +    g_free (label); + +    alsa_element_set_snd_element (element, el); + +    if (add_element (device, stream, element) == FALSE) { +        g_object_unref (element); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +add_device_switch (AlsaDevice *device, snd_mixer_elem_t *el) +{ +    g_debug ("Reading device %s switch %s (%d items)", +             mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), +             snd_mixer_selem_get_name (el), +             snd_mixer_selem_get_enum_items (el)); + +    return add_switch (device, NULL, el); +} + +static gboolean +add_stream_input_switch (AlsaDevice *device, snd_mixer_elem_t *el) +{ +    g_debug ("Reading device %s input switch %s (%d items)", +             mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), +             snd_mixer_selem_get_name (el), +             snd_mixer_selem_get_enum_items (el)); + +    return add_switch (device, device->priv->input, el); +} + +static gboolean +add_stream_output_switch (AlsaDevice *device, snd_mixer_elem_t *el) +{ +    g_debug ("Reading device %s output switch %s (%d items)", +             mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), +             snd_mixer_selem_get_name (el), +             snd_mixer_selem_get_enum_items (el)); + +    return add_switch (device, device->priv->output, el); +} + +static gboolean +add_stream_input_toggle (AlsaDevice *device, snd_mixer_elem_t *el) +{ +    AlsaToggle *toggle; + +    g_debug ("Reading device %s input toggle %s", +             mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), +             snd_mixer_selem_get_name (el)); + +    toggle = create_toggle (device, el, ALSA_TOGGLE_CAPTURE); + +    if (add_element (device, device->priv->input, ALSA_ELEMENT (toggle)) == FALSE) { +        g_object_unref (toggle); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +add_stream_output_toggle (AlsaDevice *device, snd_mixer_elem_t *el) +{ +    AlsaToggle *toggle; + +    g_debug ("Reading device %s output toggle %s", +             mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), +             snd_mixer_selem_get_name (el)); + +    toggle = create_toggle (device, el, ALSA_TOGGLE_PLAYBACK); + +    if (add_element (device, device->priv->output, ALSA_ELEMENT (toggle)) == FALSE) { +        g_object_unref (toggle); +        return FALSE; +    } +    return TRUE; +} + +static void +load_element (AlsaDevice *device, snd_mixer_elem_t *el) +{ +    gboolean cvolume = FALSE; +    gboolean pvolume = FALSE; + +    if (snd_mixer_selem_is_enumerated (el) == 1) { +        gboolean cenum = FALSE; +        gboolean penum = FALSE; + +        if (snd_mixer_selem_is_enum_capture (el) == 1) +            cenum = TRUE; +        if (snd_mixer_selem_is_enum_playback (el) == 1) +            penum = TRUE; + +        /* Enumerated controls which are not marked as capture or playback +         * are considered to be a part of the whole device, although sometimes +         * this is incorrectly reported by the driver */ +        if (cenum == FALSE && penum == FALSE) { +            add_device_switch (device, el); +        } +        else if (cenum == TRUE) +            add_stream_input_switch (device, el); +        else if (penum == TRUE) +            add_stream_output_switch (device, el); +    } + +    if (snd_mixer_selem_has_capture_volume (el) == 1 || +        snd_mixer_selem_has_common_volume (el) == 1) +        cvolume = TRUE; +    if (snd_mixer_selem_has_playback_volume (el) == 1 || +        snd_mixer_selem_has_common_volume (el) == 1) +        pvolume = TRUE; + +    if (cvolume == FALSE && pvolume == FALSE) { +        /* Control without volume and with a switch are modelled as toggles */ +        if (snd_mixer_selem_has_capture_switch (el) == 1) +            add_stream_input_toggle (device, el); + +        if (snd_mixer_selem_has_playback_switch (el) == 1) +            add_stream_output_toggle (device, el); +    } else { +        if (cvolume == TRUE) +            add_stream_input_control (device, el); +        if (pvolume == TRUE) +            add_stream_output_control (device, el); +    } +} + +static void +load_elements_by_name (AlsaDevice *device, const gchar *name) +{ +    AlsaElement *element; + +    alsa_stream_load_elements (device->priv->input, name); +    alsa_stream_load_elements (device->priv->output, name); + +    element = g_hash_table_lookup (device->priv->switches, name); +    if (element != NULL) +        alsa_element_load (element); +} + +static void +remove_elements_by_name (AlsaDevice *device, const gchar *name) +{ +    if (alsa_stream_remove_elements (device->priv->input, name) == TRUE) { +        /* Removing last stream element "removes" the stream */ +        if (alsa_stream_is_empty (device->priv->input) == TRUE) { +            const gchar *stream_name = +                mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input)); + +            g_signal_emit_by_name (G_OBJECT (device), +                                   "stream-removed", +                                   stream_name); +        } +    } + +    if (alsa_stream_remove_elements (device->priv->output, name) == TRUE) { +        /* Removing last stream element "removes" the stream */ +        if (alsa_stream_is_empty (device->priv->output) == TRUE) { +            const gchar *stream_name = +                mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output)); + +            g_signal_emit_by_name (G_OBJECT (device), +                                   "stream-removed", +                                   stream_name); +        } +    } + +    if (g_hash_table_remove (device->priv->switches, name) == TRUE) +        g_signal_emit_by_name (G_OBJECT (device), +                               "switch-removed", +                               name); +} + +static void +handle_poll (AlsaDevice *device) +{ +    /* This function is called in a worker thread. It is supposed to wait for +     * ALSA events and call handle_process_events(). Processing the events might +     * result in emitting the CLOSED signal and unreffing the instance in the +     * owner, so keep an extra reference during the lifetime of the thread. */ +    g_object_ref (device); + +    while (TRUE) { +        gint ret = snd_mixer_wait (device->priv->handle, -1); +        if (ret < 0) { +            if (ret == EINTR) +                continue; +            break; +        } + +        device->priv->events_pending = TRUE; + +        /* Process the events in the main thread because most events end up +         * emitting signals */ +        g_main_context_invoke (device->priv->context, +                               (GSourceFunc) handle_process_events, +                               device); + +        g_mutex_lock (&device->priv->mutex); + +        /* Use a GCond to wait until the events are processed. The processing +         * function may be called any time later in the main loop and snd_mixer_wait() +         * returns instantly while there are pending events. Without the wait, +         * g_main_context_invoke() could be called repeatedly to create idle sources +         * until the first idle source function is called. */ +        while (device->priv->events_pending == TRUE) +            g_cond_wait (&device->priv->cond, &device->priv->mutex); + +        g_mutex_unlock (&device->priv->mutex); + +        /* Exit the thread if the processing function closed the device */ +        if (device->priv->handle == NULL) +            break; +    } + +    g_debug ("Terminating poll thread for device %s", +             mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); + +    g_object_unref (device); +} + +static gboolean +handle_process_events (AlsaDevice *device) +{ +    g_mutex_lock (&device->priv->mutex); + +    if (device->priv->handle != NULL) { +        gint ret = snd_mixer_handle_events (device->priv->handle); +        if (ret < 0) +            close_device (device); +    } + +    device->priv->events_pending = FALSE; + +    g_cond_signal (&device->priv->cond); +    g_mutex_unlock (&device->priv->mutex); + +    return G_SOURCE_REMOVE; +} + +/* ALSA has a per-mixer callback and per-element callback, per-mixer callback + * is only used for added elements and per-element callback for all the + * other messages (no, the documentation doesn't say anything about that). */ +static int +handle_callback (snd_mixer_t *handle, guint mask, snd_mixer_elem_t *el) +{ +    if (mask & SND_CTL_EVENT_MASK_ADD) { +        AlsaDevice *device = snd_mixer_get_callback_private (handle); + +        load_element (device, el); +    } +    return 0; +} + +static int +handle_element_callback (snd_mixer_elem_t *el, guint mask) +{ +    AlsaDevice *device; +    gchar      *name; + +    device = snd_mixer_elem_get_callback_private (el); +    name = get_element_name (el); + +    if (mask == SND_CTL_EVENT_MASK_REMOVE) { +        /* Make sure this function is not called again with the element */ +        snd_mixer_elem_set_callback_private (el, NULL); +        snd_mixer_elem_set_callback (el, NULL); + +        remove_elements_by_name (device, name); +    } else { +        if (mask & SND_CTL_EVENT_MASK_INFO) { +            remove_elements_by_name (device, name); +            load_element (device, el); +        } +        if (mask & SND_CTL_EVENT_MASK_VALUE) +            load_elements_by_name (device, name); +    } +    g_free (name); + +    return 0; +} + +static void +close_device (AlsaDevice *device) +{ +    if (device->priv->handle != NULL) { +        snd_mixer_close (device->priv->handle); +        device->priv->handle = NULL; +    } + +    /* This signal tells the owner that the device has been closed voluntarily +     * from within the instance */ +    g_signal_emit (G_OBJECT (device), signals[CLOSED], 0); +} + +static gchar * +get_element_name (snd_mixer_elem_t *el) +{ +    return g_strdup_printf ("%s-%d", +                            snd_mixer_selem_get_name (el), +                            snd_mixer_selem_get_index (el)); +} + +static void +get_control_info (snd_mixer_elem_t           *el, +                  gchar                     **name, +                  gchar                     **label, +                  MateMixerStreamControlRole *role) +{ +    MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; +    const gchar               *n; +    const gchar               *l = NULL; +    gint                       i; + +    n = snd_mixer_selem_get_name (el); + +    for (i = 0; alsa_controls[i].name != NULL; i++) +        if (strcmp (n, alsa_controls[i].name) == 0) { +            l = alsa_controls[i].label; +            r = alsa_controls[i].role; +            break; +        } + +    *name = get_element_name (el); +    if (l != NULL) +        *label = g_strdup (l); +    else +        *label = g_strdup (n); + +    *role = r; +} + +static void +get_switch_info (snd_mixer_elem_t           *el, +                 gchar                     **name, +                 gchar                     **label) +{ +    // MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; +    const gchar               *n; +    const gchar               *l = NULL; +    // gint                       i; + +    n = snd_mixer_selem_get_name (el); + +    // TODO provide translated label and flags + +/* +    for (i = 0; alsa_controls[i].name != NULL; i++) +        if (strcmp (n, alsa_controls[i].name) == 0) { +            l = alsa_controls[i].label; +            r = alsa_controls[i].role; +            break; +        } +*/ +    *name = get_element_name (el); +    if (l != NULL) +        *label = g_strdup (l); +    else +        *label = g_strdup (n); + +    // *role = r; +} diff --git a/backends/alsa/alsa-device.h b/backends/alsa/alsa-device.h new file mode 100644 index 0000000..3b3c970 --- /dev/null +++ b/backends/alsa/alsa-device.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_DEVICE_H +#define ALSA_DEVICE_H + +#include <glib.h> +#include <glib-object.h> + +#include "alsa-stream.h" + +G_BEGIN_DECLS + +#define ALSA_TYPE_DEVICE                        \ +        (alsa_device_get_type ()) +#define ALSA_DEVICE(o)                          \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_DEVICE, AlsaDevice)) +#define ALSA_IS_DEVICE(o)                       \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_DEVICE)) +#define ALSA_DEVICE_CLASS(k)                    \ +        (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_DEVICE, AlsaDeviceClass)) +#define ALSA_IS_DEVICE_CLASS(k)                 \ +        (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_DEVICE)) +#define ALSA_DEVICE_GET_CLASS(o)                \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_DEVICE, AlsaDeviceClass)) + +typedef struct _AlsaDevice         AlsaDevice; +typedef struct _AlsaDeviceClass    AlsaDeviceClass; +typedef struct _AlsaDevicePrivate  AlsaDevicePrivate; + +struct _AlsaDevice +{ +    MateMixerDevice parent; + +    /*< private >*/ +    AlsaDevicePrivate *priv; +}; + +struct _AlsaDeviceClass +{ +    MateMixerDeviceClass parent_class; + +    /*< private >*/ +    void (*closed) (AlsaDevice *device); +}; + +GType       alsa_device_get_type          (void) G_GNUC_CONST; + +AlsaDevice *alsa_device_new               (const gchar *name, +                                           const gchar *label); + +gboolean    alsa_device_open              (AlsaDevice  *device); +void        alsa_device_load              (AlsaDevice  *device); + +AlsaStream *alsa_device_get_input_stream  (AlsaDevice  *device); +AlsaStream *alsa_device_get_output_stream (AlsaDevice  *device); + +G_END_DECLS + +#endif /* ALSA_DEVICE_H */ diff --git a/backends/alsa/alsa-element.c b/backends/alsa/alsa-element.c new file mode 100644 index 0000000..f925064 --- /dev/null +++ b/backends/alsa/alsa-element.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> + +#include "alsa-element.h" + +G_DEFINE_INTERFACE (AlsaElement, alsa_element, G_TYPE_OBJECT) + +static void +alsa_element_default_init (AlsaElementInterface *iface) +{ +} + +snd_mixer_elem_t * +alsa_element_get_snd_element (AlsaElement *element) +{ +    g_return_val_if_fail (ALSA_IS_ELEMENT (element), NULL); + +    return ALSA_ELEMENT_GET_INTERFACE (element)->get_snd_element (element); +} + +void +alsa_element_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el) +{ +    g_return_if_fail (ALSA_IS_ELEMENT (element)); + +    ALSA_ELEMENT_GET_INTERFACE (element)->set_snd_element (element, el); +} + +gboolean +alsa_element_load (AlsaElement *element) +{ +    g_return_val_if_fail (ALSA_IS_ELEMENT (element), FALSE); + +    return ALSA_ELEMENT_GET_INTERFACE (element)->load (element); +} diff --git a/backends/alsa/alsa-element.h b/backends/alsa/alsa-element.h new file mode 100644 index 0000000..01d30f1 --- /dev/null +++ b/backends/alsa/alsa-element.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_ELEMENT_H +#define ALSA_ELEMENT_H + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> + +G_BEGIN_DECLS + +#define ALSA_TYPE_ELEMENT                       \ +        (alsa_element_get_type ()) +#define ALSA_ELEMENT(o)                         \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_ELEMENT, AlsaElement)) +#define ALSA_IS_ELEMENT(o)                      \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_ELEMENT)) +#define ALSA_ELEMENT_GET_INTERFACE(o)           \ +        (G_TYPE_INSTANCE_GET_INTERFACE ((o), ALSA_TYPE_ELEMENT, AlsaElementInterface)) + +typedef struct _AlsaElement           AlsaElement; /* dummy object */ +typedef struct _AlsaElementInterface  AlsaElementInterface; + +struct _AlsaElementInterface +{ +    GTypeInterface parent_iface; + +    /*< private >*/ +    snd_mixer_elem_t *(*get_snd_element) (AlsaElement      *element); +    void              (*set_snd_element) (AlsaElement      *element, +                                          snd_mixer_elem_t *el); + +    gboolean          (*load)            (AlsaElement      *element); +}; + +GType             alsa_element_get_type        (void) G_GNUC_CONST; + +snd_mixer_elem_t *alsa_element_get_snd_element (AlsaElement      *element); +void              alsa_element_set_snd_element (AlsaElement      *element, +                                                snd_mixer_elem_t *el); + +gboolean          alsa_element_load            (AlsaElement      *element); + +G_END_DECLS + +#endif /* ALSA_ELEMENT_H */ diff --git a/backends/alsa/alsa-stream-control.c b/backends/alsa/alsa-stream-control.c new file mode 100644 index 0000000..bc7a937 --- /dev/null +++ b/backends/alsa/alsa-stream-control.c @@ -0,0 +1,739 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "alsa-constants.h" +#include "alsa-element.h" +#include "alsa-stream-control.h" + +struct _AlsaStreamControlPrivate +{ +    AlsaControlData   data; +    guint32           channel_mask; +    snd_mixer_elem_t *element; +}; + +static void alsa_element_interface_init    (AlsaElementInterface   *iface); + +static void alsa_stream_control_class_init (AlsaStreamControlClass *klass); +static void alsa_stream_control_init       (AlsaStreamControl      *control); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (AlsaStreamControl, alsa_stream_control, +                                  MATE_MIXER_TYPE_STREAM_CONTROL, +                                  G_IMPLEMENT_INTERFACE (ALSA_TYPE_ELEMENT, +                                                         alsa_element_interface_init)) + +static snd_mixer_elem_t *       alsa_stream_control_get_snd_element      (AlsaElement             *element); +static void                     alsa_stream_control_set_snd_element      (AlsaElement             *element, +                                                                          snd_mixer_elem_t        *el); + +static gboolean                 alsa_stream_control_load                 (AlsaElement             *element); + +static gboolean                 alsa_stream_control_set_mute             (MateMixerStreamControl  *mmsc, +                                                                          gboolean                 mute); + +static guint                    alsa_stream_control_get_num_channels     (MateMixerStreamControl  *mmsc); + +static guint                    alsa_stream_control_get_volume           (MateMixerStreamControl  *mmsc); + +static gboolean                 alsa_stream_control_set_volume           (MateMixerStreamControl  *mmsc, +                                                                          guint                    volume); + +static gdouble                  alsa_stream_control_get_decibel          (MateMixerStreamControl  *mmsc); + +static gboolean                 alsa_stream_control_set_decibel          (MateMixerStreamControl  *mmsc, +                                                                          gdouble                  decibel); + +static gboolean                 alsa_stream_control_has_channel_position (MateMixerStreamControl  *mmsc, +                                                                          MateMixerChannelPosition position); +static MateMixerChannelPosition alsa_stream_control_get_channel_position (MateMixerStreamControl  *mmsc, +                                                                          guint                    channel); + +static guint                    alsa_stream_control_get_channel_volume   (MateMixerStreamControl  *mmsc, +                                                                          guint                    channel); +static gboolean                 alsa_stream_control_set_channel_volume   (MateMixerStreamControl  *mmsc, +                                                                          guint                    channel, +                                                                          guint                    volume); + +static gdouble                  alsa_stream_control_get_channel_decibel  (MateMixerStreamControl  *mmsc, +                                                                          guint                    channel); +static gboolean                 alsa_stream_control_set_channel_decibel  (MateMixerStreamControl  *mmsc, +                                                                          guint                    channel, +                                                                          gdouble                  decibel); + +static gboolean                 alsa_stream_control_set_balance          (MateMixerStreamControl  *mmsc, +                                                                          gfloat                   balance); + +static gboolean                 alsa_stream_control_set_fade             (MateMixerStreamControl  *mmsc, +                                                                          gfloat                   fade); + +static guint                    alsa_stream_control_get_min_volume       (MateMixerStreamControl  *mmsc); +static guint                    alsa_stream_control_get_max_volume       (MateMixerStreamControl  *mmsc); +static guint                    alsa_stream_control_get_normal_volume    (MateMixerStreamControl  *mmsc); +static guint                    alsa_stream_control_get_base_volume      (MateMixerStreamControl  *mmsc); + +static void                     control_data_get_average_left_right      (AlsaControlData         *data, +                                                                          guint                   *left, +                                                                          guint                   *right); +static void                     control_data_get_average_front_back      (AlsaControlData         *data, +                                                                          guint                   *front, +                                                                          guint                   *back); + +static gfloat                   control_data_get_balance                 (AlsaControlData         *data); +static gfloat                   control_data_get_fade                    (AlsaControlData         *data); + +static void +alsa_element_interface_init (AlsaElementInterface *iface) +{ +    iface->get_snd_element = alsa_stream_control_get_snd_element; +    iface->set_snd_element = alsa_stream_control_set_snd_element; +    iface->load            = alsa_stream_control_load; +} + +static void +alsa_stream_control_class_init (AlsaStreamControlClass *klass) +{ +    MateMixerStreamControlClass *control_class; + +    control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); + +    control_class->set_mute             = alsa_stream_control_set_mute; +    control_class->get_num_channels     = alsa_stream_control_get_num_channels; +    control_class->get_volume           = alsa_stream_control_get_volume; +    control_class->set_volume           = alsa_stream_control_set_volume; +    control_class->get_decibel          = alsa_stream_control_get_decibel; +    control_class->set_decibel          = alsa_stream_control_set_decibel; +    control_class->has_channel_position = alsa_stream_control_has_channel_position; +    control_class->get_channel_position = alsa_stream_control_get_channel_position; +    control_class->get_channel_volume   = alsa_stream_control_get_channel_volume; +    control_class->set_channel_volume   = alsa_stream_control_set_channel_volume; +    control_class->get_channel_decibel  = alsa_stream_control_get_channel_decibel; +    control_class->set_channel_decibel  = alsa_stream_control_set_channel_decibel; +    control_class->set_balance          = alsa_stream_control_set_balance; +    control_class->set_fade             = alsa_stream_control_set_fade; +    control_class->get_min_volume       = alsa_stream_control_get_min_volume; +    control_class->get_max_volume       = alsa_stream_control_get_max_volume; +    control_class->get_normal_volume    = alsa_stream_control_get_normal_volume; +    control_class->get_base_volume      = alsa_stream_control_get_base_volume; + +    g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (AlsaStreamControlPrivate)); +} + +static void +alsa_stream_control_init (AlsaStreamControl *control) +{ +    control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, +                                                 ALSA_TYPE_STREAM_CONTROL, +                                                 AlsaStreamControlPrivate); +} + +AlsaControlData * +alsa_stream_control_get_data (AlsaStreamControl *control) +{ +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), NULL); + +    return &control->priv->data; +} + +void +alsa_stream_control_set_data (AlsaStreamControl *control, AlsaControlData *data) +{ +    MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS; +    MateMixerStreamControl     *mmsc; + +    g_return_if_fail (ALSA_IS_STREAM_CONTROL (control)); +    g_return_if_fail (data != NULL); + +    mmsc = MATE_MIXER_STREAM_CONTROL (control); + +    g_object_freeze_notify (G_OBJECT (control)); + +    if (data->channels > 0) { +        if (data->switch_usable == TRUE) { +            flags |= MATE_MIXER_STREAM_CONTROL_HAS_MUTE; +            if (data->active == TRUE) +                flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_MUTE; +        } +        flags |= MATE_MIXER_STREAM_CONTROL_HAS_VOLUME; +        if (data->active == TRUE) +            flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME; +    } +    if (data->max_decibel > -MATE_MIXER_INFINITY) +        flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; + +    control->priv->data = *data; +    control->priv->channel_mask = _mate_mixer_create_channel_mask (data->c, data->channels); + +    if (data->volume_joined == FALSE) { +        if (MATE_MIXER_CHANNEL_MASK_HAS_LEFT (control->priv->channel_mask) && +            MATE_MIXER_CHANNEL_MASK_HAS_RIGHT (control->priv->channel_mask)) +            flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + +        if (MATE_MIXER_CHANNEL_MASK_HAS_FRONT (control->priv->channel_mask) && +            MATE_MIXER_CHANNEL_MASK_HAS_BACK (control->priv->channel_mask)) +            flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE; +    } + +    _mate_mixer_stream_control_set_flags (mmsc, flags); + +    if (data->switch_usable == TRUE) { +        gboolean mute; + +        /* If the mute switch is joined, all the channels get the same value, +         * otherwise the element has per-channel mute, which we don't support. +         * In that case, treat the control as unmuted if any channel is +         * unmuted. */ +        if (data->channels == 1 || data->switch_joined == TRUE) { +            mute = data->m[0]; +        } else { +            gint i; +            mute = TRUE; +            for (i = 0; i < data->channels; i++) +                if (data->m[i] == FALSE) { +                    mute = FALSE; +                    break; +                } +        } +        _mate_mixer_stream_control_set_mute (mmsc, mute); +    } else +        _mate_mixer_stream_control_set_mute (mmsc, FALSE); + +    if (flags & MATE_MIXER_STREAM_CONTROL_CAN_BALANCE) +        _mate_mixer_stream_control_set_balance (mmsc, control_data_get_balance (data)); +    if (flags & MATE_MIXER_STREAM_CONTROL_CAN_FADE) +        _mate_mixer_stream_control_set_fade (mmsc, control_data_get_fade (data)); + +    g_object_thaw_notify (G_OBJECT (control)); +} + +static snd_mixer_elem_t * +alsa_stream_control_get_snd_element (AlsaElement *element) +{ +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (element), NULL); + +    return ALSA_STREAM_CONTROL (element)->priv->element; +} + +static void +alsa_stream_control_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el) +{ +    g_return_if_fail (ALSA_IS_STREAM_CONTROL (element)); +    g_return_if_fail (el != NULL); + +    ALSA_STREAM_CONTROL (element)->priv->element = el; +} + +static gboolean +alsa_stream_control_load (AlsaElement *element) +{ +    AlsaStreamControl *control; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (element), FALSE); + +    control = ALSA_STREAM_CONTROL (element); + +    return ALSA_STREAM_CONTROL_GET_CLASS (control)->load (control); +} + +static gboolean +alsa_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute) +{ +    AlsaStreamControl *control; +    gboolean           change = FALSE; +    gint               i; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE); + +    control = ALSA_STREAM_CONTROL (mmsc); + +    /* If the switch is joined, only verify the first channel */ +    if (control->priv->data.switch_joined == TRUE) { +        if (control->priv->data.m[0] != mute) +            change = TRUE; +    } else { +        /* Avoid trying to set the mute if all channels are already at the +         * selected mute value */ +        for (i = 0; i < control->priv->data.channels; i++) +            if (control->priv->data.m[i] != mute) { +                change = TRUE; +                break; +            } +    } + +    if (change == TRUE) { +        AlsaStreamControlClass *klass; + +        klass = ALSA_STREAM_CONTROL_GET_CLASS (control); +        if (klass->set_mute (control, mute) == FALSE) +            return FALSE; + +        for (i = 0; i < control->priv->data.channels; i++) +            control->priv->data.m[i] = mute; +    } +    return TRUE; +} + +static guint +alsa_stream_control_get_num_channels (MateMixerStreamControl *mmsc) +{ +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), 0); + +    return ALSA_STREAM_CONTROL (mmsc)->priv->data.channels; +} + +static guint +alsa_stream_control_get_volume (MateMixerStreamControl *mmsc) +{ +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), 0); + +    return ALSA_STREAM_CONTROL (mmsc)->priv->data.volume; +} + +static gboolean +alsa_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume) +{ +    AlsaStreamControl *control; +    gboolean           change = FALSE; +    gint               i; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE); + +    control = ALSA_STREAM_CONTROL (mmsc); +    volume  = CLAMP (volume, control->priv->data.min, control->priv->data.max); + +    /* If the volume is joined, only verify the first channel */ +    if (control->priv->data.volume_joined == TRUE) { +        if (control->priv->data.v[0] != volume) +            change = TRUE; +    } else { +        /* Avoid trying to set the volume if all channels are already at the +         * selected volume */ +        for (i = 0; i < control->priv->data.channels; i++) +            if (control->priv->data.v[i] != volume) { +                change = TRUE; +                break; +            } +    } + +    if (change == TRUE) { +        AlsaStreamControlClass *klass; + +        klass = ALSA_STREAM_CONTROL_GET_CLASS (control); +        if (klass->set_volume (control, volume) == FALSE) +            return FALSE; + +        for (i = 0; i < control->priv->data.channels; i++) +            control->priv->data.v[i] = volume; + +        g_object_notify (G_OBJECT (control), "volume"); +    } +    return TRUE; +} + +static gdouble +alsa_stream_control_get_decibel (MateMixerStreamControl *mmsc) +{ +    AlsaStreamControl      *control; +    AlsaStreamControlClass *klass; +    guint                   volume; +    gdouble                 decibel; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY); + +    control = ALSA_STREAM_CONTROL (mmsc); +    klass   = ALSA_STREAM_CONTROL_GET_CLASS (control); +    volume  = alsa_stream_control_get_volume (mmsc); + +    if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE) +        return FALSE; + +    return decibel; +} + +static gboolean +alsa_stream_control_set_decibel (MateMixerStreamControl *mmsc, gdouble decibel) +{ +    AlsaStreamControl      *control; +    AlsaStreamControlClass *klass; +    guint                   volume; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE); + +    control = ALSA_STREAM_CONTROL (mmsc); +    klass   = ALSA_STREAM_CONTROL_GET_CLASS (control); + +    if (klass->get_volume_from_decibel (control, decibel, &volume) == FALSE) +        return FALSE; + +    return alsa_stream_control_set_volume (mmsc, volume); +} + +static gboolean +alsa_stream_control_has_channel_position (MateMixerStreamControl  *mmsc, +                                          MateMixerChannelPosition position) +{ +    AlsaStreamControl *control; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE); + +    control = ALSA_STREAM_CONTROL (mmsc); + +    if MATE_MIXER_CHANNEL_MASK_HAS_CHANNEL (control->priv->channel_mask, position) +        return TRUE; +    else +        return FALSE; +} + +static MateMixerChannelPosition +alsa_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel) +{ +    AlsaStreamControl *control; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN); + +    control = ALSA_STREAM_CONTROL (mmsc); + +    if (channel >= control->priv->data.channels) +        return MATE_MIXER_CHANNEL_UNKNOWN; + +    return control->priv->data.c[channel]; +} + +static guint +alsa_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel) +{ +    AlsaStreamControl *control; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), 0); + +    control = ALSA_STREAM_CONTROL (mmsc); + +    if (channel >= control->priv->data.channels) +        return FALSE; + +    return control->priv->data.v[channel]; +} + +static gboolean +alsa_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume) +{ +    AlsaStreamControl *control; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE); + +    control = ALSA_STREAM_CONTROL (mmsc); + +    if (channel >= control->priv->data.channels) +        return FALSE; + +    /* Set volume for all channels at once when channels are joined */ +    if (control->priv->data.volume_joined == TRUE) +        return alsa_stream_control_set_volume (mmsc, volume); + +    if (volume != control->priv->data.v[channel]) { +        AlsaStreamControlClass *klass; + +        /* Convert channel index to ALSA channel position and make sure it is valid */ +        snd_mixer_selem_channel_id_t c = alsa_channel_map_to[control->priv->data.c[channel]]; +        if G_UNLIKELY (c == SND_MIXER_SCHN_UNKNOWN) { +            g_warn_if_reached (); +            return FALSE; +        } + +        klass = ALSA_STREAM_CONTROL_GET_CLASS (control); +        if (klass->set_channel_volume (control, c, volume) == FALSE) +            return FALSE; + +        control->priv->data.v[channel] = volume; + +        g_object_notify (G_OBJECT (control), "volume"); +    } +    return TRUE; +} + +static gdouble +alsa_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint channel) +{ +    AlsaStreamControl      *control; +    AlsaStreamControlClass *klass; +    guint                   volume; +    gdouble                 decibel; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY); + +    control = ALSA_STREAM_CONTROL (mmsc); + +    if (channel >= control->priv->data.channels) +        return FALSE; + +    klass  = ALSA_STREAM_CONTROL_GET_CLASS (control); +    volume = control->priv->data.v[channel]; + +    if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE) +        return FALSE; + +    return decibel; +} + +static gboolean +alsa_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc, +                                         guint                   channel, +                                         gdouble                 decibel) +{ +    AlsaStreamControl      *control; +    AlsaStreamControlClass *klass; +    guint                   volume; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE); + +    control = ALSA_STREAM_CONTROL (mmsc); +    klass   = ALSA_STREAM_CONTROL_GET_CLASS (control); + +    if (klass->get_volume_from_decibel (control, decibel, &volume) == FALSE) +        return FALSE; + +    return alsa_stream_control_set_channel_volume (mmsc, channel, volume); +} + +static gboolean +alsa_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance) +{ +    AlsaStreamControlClass *klass; +    AlsaStreamControl      *control; +    AlsaControlData        *data; +    guint                   left, +                            right; +    guint                   nleft, +                            nright; +    guint                   max; +    guint                   channel; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE); + +    control = ALSA_STREAM_CONTROL (mmsc); +    klass   = ALSA_STREAM_CONTROL_GET_CLASS (control); + +    data = &control->priv->data; +    control_data_get_average_left_right (data, &left, &right); + +    max = MAX (left, right); +    if (balance <= 0) { +        nright = (balance + 1.0f) * max; +        nleft  = max; +    } else { +        nleft  = (1.0f - balance) * max; +        nright = max; +    } + +    for (channel = 0; channel < data->channels; channel++) { +        gboolean lc = MATE_MIXER_IS_LEFT_CHANNEL (data->c[channel]); +        gboolean rc = MATE_MIXER_IS_RIGHT_CHANNEL (data->c[channel]); + +        if (lc == TRUE || rc == TRUE) { +            guint volume; +            if (lc == TRUE) { +                if (left == 0) +                    volume = nleft; +                else +                    volume = CLAMP (((guint64) data->v[channel] * (guint64) nleft) / (guint64) left, +                                    data->min, +                                    data->max); +            } else { +                if (right == 0) +                    volume = nright; +                else +                    volume = CLAMP (((guint64) data->v[channel] * (guint64) nright) / (guint64) right, +                                    data->min, +                                    data->max); +            } + +            if (klass->set_channel_volume (control, +                                           alsa_channel_map_to[data->c[channel]], +                                           volume) == TRUE) +                data->v[channel] = volume; +        } +    } +    return TRUE; +} + +static gboolean +alsa_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade) +{ +    AlsaStreamControlClass *klass; +    AlsaStreamControl      *control; +    AlsaControlData        *data; +    guint                   front, +                            back; +    guint                   nfront, +                            nback; +    guint                   max; +    guint                   channel; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (mmsc), FALSE); + +    control = ALSA_STREAM_CONTROL (mmsc); +    klass   = ALSA_STREAM_CONTROL_GET_CLASS (control); + +    data = &control->priv->data; +    control_data_get_average_front_back (data, &front, &back); + +    max = MAX (front, back); +    if (fade <= 0) { +        nback  = (fade + 1.0f) * max; +        nfront = max; +    } else { +        nfront = (1.0f - fade) * max; +        nback  = max; +    } + +    for (channel = 0; channel < data->channels; channel++) { +        gboolean fc = MATE_MIXER_IS_FRONT_CHANNEL (data->c[channel]); +        gboolean bc = MATE_MIXER_IS_BACK_CHANNEL (data->c[channel]); + +        if (fc == TRUE || bc == TRUE) { +            guint volume; +            if (fc == TRUE) { +                if (front == 0) +                    volume = nfront; +                else +                    volume = CLAMP (((guint64) data->v[channel] * (guint64) nfront) / (guint64) front, +                                    data->min, +                                    data->max); +            } else { +                if (back == 0) +                    volume = nback; +                else +                    volume = CLAMP (((guint64) data->v[channel] * (guint64) nback) / (guint64) back, +                                    data->min, +                                    data->max); +            } + +            if (klass->set_channel_volume (control, +                                           alsa_channel_map_to[data->c[channel]], +                                           volume) == TRUE) +                data->v[channel] = volume; +        } +    } +    return TRUE; +} + +static guint +alsa_stream_control_get_min_volume (MateMixerStreamControl *msc) +{ +    return ALSA_STREAM_CONTROL (msc)->priv->data.min; +} + +static guint +alsa_stream_control_get_max_volume (MateMixerStreamControl *msc) +{ +    return ALSA_STREAM_CONTROL (msc)->priv->data.max; +} + +static guint +alsa_stream_control_get_normal_volume (MateMixerStreamControl *msc) +{ +    return ALSA_STREAM_CONTROL (msc)->priv->data.max; +} + +static guint +alsa_stream_control_get_base_volume (MateMixerStreamControl *msc) +{ +    return ALSA_STREAM_CONTROL (msc)->priv->data.max; +} + +static void +control_data_get_average_left_right (AlsaControlData *data, guint *left, guint *right) +{ +    guint l = 0, +          r = 0; +    guint nl = 0, +          nr = 0; +    guint channel; + +    for (channel = 0; channel < data->channels; channel++) +        if MATE_MIXER_IS_LEFT_CHANNEL (data->c[channel]) { +            l += data->v[channel]; +            nl++; +        } +        else if MATE_MIXER_IS_RIGHT_CHANNEL (data->c[channel]) { +            r += data->v[channel]; +            nr++; +        } + +    *left  = (nl > 0) ? l / nl : data->max; +    *right = (nr > 0) ? r / nr : data->max; +} + +static void +control_data_get_average_front_back (AlsaControlData *data, guint *front, guint *back) +{ +    guint f = 0, +          b = 0; +    guint nf = 0, +          nb = 0; +    guint channel; + +    for (channel = 0; channel < data->channels; channel++) +        if MATE_MIXER_IS_FRONT_CHANNEL (data->c[channel]) { +            f += data->v[channel]; +            nf++; +        } +        else if MATE_MIXER_IS_RIGHT_CHANNEL (data->c[channel]) { +            b += data->v[channel]; +            nb++; +        } + +    *front = (nf > 0) ? f / nf : data->max; +    *back  = (nb > 0) ? b / nb : data->max; +} + +static gfloat +control_data_get_balance (AlsaControlData *data) +{ +    guint left; +    guint right; + +    control_data_get_average_left_right (data, &left, &right); +    if (left == right) +        return 0.0f; + +    if (left > right) +        return -1.0f + ((gfloat) right / (gfloat) left); +    else +        return +1.0f - ((gfloat) left / (gfloat) right); +} + +static gfloat +control_data_get_fade (AlsaControlData *data) +{ +    guint front; +    guint back; + +    control_data_get_average_front_back (data, &front, &back); +    if (front == back) +        return 0.0f; + +    if (front > back) +        return -1.0f + ((gfloat) back / (gfloat) front); +    else +        return +1.0f - ((gfloat) front / (gfloat) back); +} diff --git a/backends/alsa/alsa-stream-control.h b/backends/alsa/alsa-stream-control.h new file mode 100644 index 0000000..f9ac6b6 --- /dev/null +++ b/backends/alsa/alsa-stream-control.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_STREAM_CONTROL_H +#define ALSA_STREAM_CONTROL_H + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> +#include <libmatemixer/matemixer.h> + +G_BEGIN_DECLS + +typedef struct { +    gboolean                 active; +    MateMixerChannelPosition c[MATE_MIXER_CHANNEL_MAX]; +    guint                    v[MATE_MIXER_CHANNEL_MAX]; +    gboolean                 m[MATE_MIXER_CHANNEL_MAX]; +    guint                    volume; +    gboolean                 volume_joined; +    gboolean                 switch_usable; +    gboolean                 switch_joined; +    guint                    min; +    guint                    max; +    gdouble                  min_decibel; +    gdouble                  max_decibel; +    guint                    channels; +} AlsaControlData; + +extern const MateMixerChannelPosition     alsa_channel_map_from[SND_MIXER_SCHN_LAST]; +extern const snd_mixer_selem_channel_id_t alsa_channel_map_to[MATE_MIXER_CHANNEL_MAX]; + +#define ALSA_TYPE_STREAM_CONTROL                \ +        (alsa_stream_control_get_type ()) +#define ALSA_STREAM_CONTROL(o)                  \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_STREAM_CONTROL, AlsaStreamControl)) +#define ALSA_IS_STREAM_CONTROL(o)               \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_STREAM_CONTROL)) +#define ALSA_STREAM_CONTROL_CLASS(k)            \ +        (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_STREAM_CONTROL, AlsaStreamControlClass)) +#define ALSA_IS_STREAM_CONTROL_CLASS(k)         \ +        (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_STREAM_CONTROL)) +#define ALSA_STREAM_CONTROL_GET_CLASS(o)        \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_STREAM_CONTROL, AlsaStreamControlClass)) + +typedef struct _AlsaStreamControl         AlsaStreamControl; +typedef struct _AlsaStreamControlClass    AlsaStreamControlClass; +typedef struct _AlsaStreamControlPrivate  AlsaStreamControlPrivate; + +struct _AlsaStreamControl +{ +    MateMixerStreamControl parent; + +    /*< private >*/ +    AlsaStreamControlPrivate *priv; + +}; + +struct _AlsaStreamControlClass +{ +    MateMixerStreamControlClass parent_class; + +    /*< private >*/ +    gboolean (*load)                    (AlsaStreamControl           *control); + +    gboolean (*set_mute)                (AlsaStreamControl           *control, +                                         gboolean                     mute); + +    gboolean (*set_volume)              (AlsaStreamControl           *control, +                                         guint                        volume); + +    gboolean (*set_channel_volume)      (AlsaStreamControl           *control, +                                         snd_mixer_selem_channel_id_t channel, +                                         guint                        volume); + +    gboolean (*get_volume_from_decibel) (AlsaStreamControl           *control, +                                         gdouble                      decibel, +                                         guint                       *volume); + +    gboolean (*get_decibel_from_volume) (AlsaStreamControl           *control, +                                         guint                        volume, +                                         gdouble                     *decibel); +}; + +GType              alsa_stream_control_get_type        (void) G_GNUC_CONST; + +AlsaControlData *  alsa_stream_control_get_data        (AlsaStreamControl         *control); + +void               alsa_stream_control_set_data        (AlsaStreamControl         *control, +                                                        AlsaControlData           *data); + +gboolean           alsa_stream_control_set_role        (AlsaStreamControl         *control, +                                                        MateMixerStreamControlRole role); + +G_END_DECLS + +#endif /* ALSA_STREAM_CONTROL_H */ diff --git a/backends/alsa/alsa-stream-input-control.c b/backends/alsa/alsa-stream-input-control.c new file mode 100644 index 0000000..2ef0c42 --- /dev/null +++ b/backends/alsa/alsa-stream-input-control.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "alsa-element.h" +#include "alsa-stream-control.h" +#include "alsa-stream-input-control.h" + +static void alsa_stream_input_control_class_init (AlsaStreamInputControlClass *klass); +static void alsa_stream_input_control_init       (AlsaStreamInputControl      *control); + +G_DEFINE_TYPE (AlsaStreamInputControl, alsa_stream_input_control, ALSA_TYPE_STREAM_CONTROL) + +static gboolean alsa_stream_input_control_load                    (AlsaStreamControl           *control); + +static gboolean alsa_stream_input_control_set_mute                (AlsaStreamControl           *control, +                                                                   gboolean                     mute); + +static gboolean alsa_stream_input_control_set_volume              (AlsaStreamControl           *control, +                                                                   guint                        volume); + +static gboolean alsa_stream_input_control_set_channel_volume      (AlsaStreamControl           *control, +                                                                   snd_mixer_selem_channel_id_t channel, +                                                                   guint                        volume); + +static gboolean alsa_stream_input_control_get_volume_from_decibel (AlsaStreamControl           *control, +                                                                   gdouble                      decibel, +                                                                   guint                       *volume); + +static gboolean alsa_stream_input_control_get_decibel_from_volume (AlsaStreamControl           *control, +                                                                   guint                        volume, +                                                                   gdouble                     *decibel); + +static void     read_volume_data (snd_mixer_elem_t *el, +                                  AlsaControlData  *data); + +static void +alsa_stream_input_control_class_init (AlsaStreamInputControlClass *klass) +{ +    AlsaStreamControlClass *control_class; + +    control_class = ALSA_STREAM_CONTROL_CLASS (klass); + +    control_class->load                    = alsa_stream_input_control_load; +    control_class->set_mute                = alsa_stream_input_control_set_mute; +    control_class->set_volume              = alsa_stream_input_control_set_volume; +    control_class->set_channel_volume      = alsa_stream_input_control_set_channel_volume; +    control_class->get_volume_from_decibel = alsa_stream_input_control_get_volume_from_decibel; +    control_class->get_decibel_from_volume = alsa_stream_input_control_get_decibel_from_volume; +} + +static void +alsa_stream_input_control_init (AlsaStreamInputControl *control) +{ +} + +AlsaStreamControl * +alsa_stream_input_control_new (const gchar               *name, +                               const gchar               *label, +                               MateMixerStreamControlRole role) +{ +    return g_object_new (ALSA_TYPE_STREAM_INPUT_CONTROL, +                         "name", name, +                         "label", label, +                         "role", role, +                         NULL); +} + +static gboolean +alsa_stream_input_control_load (AlsaStreamControl *control) +{ +    AlsaControlData   data; +    snd_mixer_elem_t *el; + +    g_return_val_if_fail (ALSA_IS_STREAM_INPUT_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    /* Expect that the element has a volume control */ +    if G_UNLIKELY (snd_mixer_selem_has_capture_volume (el) == 0 && +                   snd_mixer_selem_has_common_volume (el) == 0) { +        g_warn_if_reached (); +        return FALSE; +    } + +    memset (&data, 0, sizeof (AlsaControlData)); + +    /* We model any control switch as mute */ +    if (snd_mixer_selem_has_capture_switch (el) == 1 || +        snd_mixer_selem_has_common_switch (el) == 1) +        data.switch_usable = TRUE; + +    data.active = snd_mixer_selem_is_active (el); + +    /* Read the volume data but do not error out if it fails, since ALSA reports +     * the control to have a volume, expect the control to match what we need - slider +     * with an optional mute toggle. +     * If it fails to read the volume data, just treat it as a volumeless control */ +    read_volume_data (el, &data); + +    alsa_stream_control_set_data (control, &data); +    return TRUE; +} + +static gboolean +alsa_stream_input_control_set_mute (AlsaStreamControl *control, gboolean mute) +{ +    snd_mixer_elem_t *el; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    /* Set the switch for all channels */ +    ret = snd_mixer_selem_set_capture_switch_all (el, !mute); +    if (ret < 0) { +        g_warning ("Failed to set capture switch: %s", snd_strerror (ret)); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +alsa_stream_input_control_set_volume (AlsaStreamControl *control, guint volume) +{ +    snd_mixer_elem_t *el; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    /* Set the volume for all channels */ +    ret = snd_mixer_selem_set_capture_volume_all (el, volume); +    if (ret < 0) { +        g_warning ("Failed to set volume: %s", snd_strerror (ret)); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +alsa_stream_input_control_set_channel_volume (AlsaStreamControl           *control, +                                              snd_mixer_selem_channel_id_t channel, +                                              guint                        volume) +{ +    snd_mixer_elem_t *el; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    /* Set the volume for a single channels, the volume may still be "joined" and +     * set all the channels by itself */ +    ret = snd_mixer_selem_set_capture_volume (el, channel, volume); +    if (ret < 0) { +        g_warning ("Failed to set channel volume: %s", snd_strerror (ret)); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +alsa_stream_input_control_get_volume_from_decibel (AlsaStreamControl *control, +                                                   gdouble            decibel, +                                                   guint             *volume) +{ +    snd_mixer_elem_t *el; +    glong             value; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    ret = snd_mixer_selem_ask_capture_dB_vol (el, (glong) (decibel * 100), 0, &value); +    if (ret < 0) { +        g_warning ("Failed to convert volume: %s", snd_strerror (ret)); +        return FALSE; +    } + +    *volume = value; +    return TRUE; +} + +static gboolean +alsa_stream_input_control_get_decibel_from_volume (AlsaStreamControl *control, +                                                   guint              volume, +                                                   gdouble           *decibel) +{ +    snd_mixer_elem_t *el; +    glong             value; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    ret = snd_mixer_selem_ask_capture_vol_dB (el, (glong) volume, &value); +    if (ret < 0) { +        g_warning ("Failed to convert volume: %s", snd_strerror (ret)); +        return FALSE; +    } + +    *decibel = value / 100.0; +    return TRUE; +} + +static void +read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data) +{ +    glong volume; +    glong min, max; +    gint  ret; +    gint  i; + +    /* Read volume ranges, this call should never fail on valid input */ +    ret = snd_mixer_selem_get_capture_volume_range (el, &min, &max); +    if G_UNLIKELY (ret < 0) { +        g_warning ("Failed to read capture volume range: %s", snd_strerror (ret)); +        return; +    } +    data->min = (guint) min; +    data->max = (guint) max; + +    /* This fails when decibels are not supported */ +    ret = snd_mixer_selem_get_capture_dB_range (el, &min, &max); +    if (ret == 0) { +        data->min_decibel = min / 100.0; +        data->max_decibel = max / 100.0; +    } else +        data->min_decibel = data->max_decibel = -MATE_MIXER_INFINITY; + +    for (i = 0; i < MATE_MIXER_CHANNEL_MAX; i++) +        data->v[i] = data->min; + +    data->volume = data->min; +    data->volume_joined = snd_mixer_selem_has_capture_volume_joined (el); + +    if (data->switch_usable == TRUE) +        data->switch_joined = snd_mixer_selem_has_capture_switch_joined (el); + +    if (snd_mixer_selem_is_capture_mono (el) == 1) { +        /* Special handling for single channel controls */ +        ret = snd_mixer_selem_get_capture_volume (el, SND_MIXER_SCHN_MONO, &volume); +        if (ret == 0) { +            data->channels = 1; + +            data->c[0] = MATE_MIXER_CHANNEL_MONO; +            data->v[0] = data->volume = (guint) volume; +        } else { +            g_warning ("Failed to read capture volume: %s", snd_strerror (ret)); +        } + +        if (data->switch_usable == TRUE) { +            gint value; + +            ret = snd_mixer_selem_get_capture_switch (el, SND_MIXER_SCHN_MONO, &value); +            if G_LIKELY (ret == 0) +                data->m[0] = !value; +        } +    } else { +        snd_mixer_selem_channel_id_t channel; + +        /* We use numeric channel indices, but ALSA only works with channel +         * positions, go over all the positions supported by ALSA and create +         * a list of channels */ +         for (channel = 0; channel < SND_MIXER_SCHN_LAST; channel++) { +            if (snd_mixer_selem_has_capture_channel (el, channel) == 0) +                continue; + +            if (data->switch_usable == TRUE) { +                gint value; + +                ret = snd_mixer_selem_get_capture_switch (el, channel, &value); +                if (ret == 0) +                    data->m[channel] = !value; +            } + +            ret = snd_mixer_selem_get_capture_volume (el, channel, &volume); +            if (ret < 0) { +                g_warning ("Failed to read capture volume: %s", snd_strerror (ret)); +                continue; +            } +            data->channels++; + +            /* The single value volume is the highest channel volume */ +            if (data->volume < volume) +                data->volume = volume; + +            data->c[channel] = alsa_channel_map_from[channel]; +            data->v[channel] = (guint) volume; +        } +    } +} diff --git a/backends/alsa/alsa-stream-input-control.h b/backends/alsa/alsa-stream-input-control.h new file mode 100644 index 0000000..c427e3c --- /dev/null +++ b/backends/alsa/alsa-stream-input-control.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_STREAM_INPUT_CONTROL_H +#define ALSA_STREAM_INPUT_CONTROL_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "alsa-stream-control.h" + +G_BEGIN_DECLS + +#define ALSA_TYPE_STREAM_INPUT_CONTROL          \ +        (alsa_stream_input_control_get_type ()) +#define ALSA_STREAM_INPUT_CONTROL(o)            \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_STREAM_INPUT_CONTROL, AlsaStreamInputControl)) +#define ALSA_IS_STREAM_INPUT_CONTROL(o)         \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_STREAM_INPUT_CONTROL)) +#define ALSA_STREAM_INPUT_CONTROL_CLASS(k)      \ +        (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_STREAM_INPUT_CONTROL, AlsaStreamInputControlClass)) +#define ALSA_IS_STREAM_INPUT_CONTROL_CLASS(k)   \ +        (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_STREAM_INPUT_CONTROL)) +#define ALSA_STREAM_INPUT_CONTROL_GET_CLASS(o)  \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_STREAM_INPUT_CONTROL, AlsaStreamInputControlClass)) + +typedef struct _AlsaStreamInputControl         AlsaStreamInputControl; +typedef struct _AlsaStreamInputControlClass    AlsaStreamInputControlClass; +typedef struct _AlsaStreamInputControlPrivate  AlsaStreamInputControlPrivate; + +struct _AlsaStreamInputControl +{ +    AlsaStreamControl parent; +}; + +struct _AlsaStreamInputControlClass +{ +    AlsaStreamControlClass parent_class; +}; + +GType              alsa_stream_input_control_get_type (void) G_GNUC_CONST; + +AlsaStreamControl *alsa_stream_input_control_new      (const gchar               *name, +                                                       const gchar               *label, +                                                       MateMixerStreamControlRole role); + +G_END_DECLS + +#endif /* ALSA_STREAM_INPUT_CONTROL_H */ diff --git a/backends/alsa/alsa-stream-output-control.c b/backends/alsa/alsa-stream-output-control.c new file mode 100644 index 0000000..5a3e6b3 --- /dev/null +++ b/backends/alsa/alsa-stream-output-control.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "alsa-element.h" +#include "alsa-stream-control.h" +#include "alsa-stream-output-control.h" + +static void alsa_stream_output_control_class_init (AlsaStreamOutputControlClass *klass); +static void alsa_stream_output_control_init       (AlsaStreamOutputControl      *control); + +G_DEFINE_TYPE (AlsaStreamOutputControl, alsa_stream_output_control, ALSA_TYPE_STREAM_CONTROL) + +static gboolean alsa_stream_output_control_load                    (AlsaStreamControl           *control); + +static gboolean alsa_stream_output_control_set_mute                (AlsaStreamControl           *control, +                                                                    gboolean                     mute); + +static gboolean alsa_stream_output_control_set_volume              (AlsaStreamControl           *control, +                                                                    guint                        volume); + +static gboolean alsa_stream_output_control_set_channel_volume      (AlsaStreamControl           *control, +                                                                    snd_mixer_selem_channel_id_t channel, +                                                                    guint                        volume); + +static gboolean alsa_stream_output_control_get_volume_from_decibel (AlsaStreamControl           *control, +                                                                    gdouble                      decibel, +                                                                    guint                       *volume); + +static gboolean alsa_stream_output_control_get_decibel_from_volume (AlsaStreamControl           *control, +                                                                    guint                        volume, +                                                                    gdouble                     *decibel); + +static void     read_volume_data (snd_mixer_elem_t *el, +                                  AlsaControlData  *data); + +static void +alsa_stream_output_control_class_init (AlsaStreamOutputControlClass *klass) +{ +    AlsaStreamControlClass *control_class; + +    control_class = ALSA_STREAM_CONTROL_CLASS (klass); + +    control_class->load                    = alsa_stream_output_control_load; +    control_class->set_mute                = alsa_stream_output_control_set_mute; +    control_class->set_volume              = alsa_stream_output_control_set_volume; +    control_class->set_channel_volume      = alsa_stream_output_control_set_channel_volume; +    control_class->get_volume_from_decibel = alsa_stream_output_control_get_volume_from_decibel; +    control_class->get_decibel_from_volume = alsa_stream_output_control_get_decibel_from_volume; +} + +static void +alsa_stream_output_control_init (AlsaStreamOutputControl *control) +{ +} + +AlsaStreamControl * +alsa_stream_output_control_new (const gchar               *name, +                                const gchar               *label, +                                MateMixerStreamControlRole role) +{ +    return g_object_new (ALSA_TYPE_STREAM_OUTPUT_CONTROL, +                         "name", name, +                         "label", label, +                         "role", role, +                         NULL); +} + +static gboolean +alsa_stream_output_control_load (AlsaStreamControl *control) +{ +    AlsaControlData   data; +    snd_mixer_elem_t *el; + +    g_return_val_if_fail (ALSA_IS_STREAM_OUTPUT_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    /* Expect that the element has a volume control */ +    if G_UNLIKELY (snd_mixer_selem_has_playback_volume (el) == 0 && +                   snd_mixer_selem_has_common_volume (el) == 0) { +        g_warn_if_reached (); +        return FALSE; +    } + +    memset (&data, 0, sizeof (AlsaControlData)); + +    /* We model any control switch as mute */ +    if (snd_mixer_selem_has_playback_switch (el) == 1 || +        snd_mixer_selem_has_common_switch (el) == 1) +        data.switch_usable = TRUE; + +    data.active = snd_mixer_selem_is_active (el); + +    /* Read the volume data but do not error out if it fails, since ALSA reports +     * the control to have a volume, expect the control to match what we need - slider +     * with an optional mute toggle. +     * If it fails to read the volume data, just treat it as a volumeless control */ +    read_volume_data (el, &data); + +    alsa_stream_control_set_data (control, &data); +    return TRUE; +} + +static gboolean +alsa_stream_output_control_set_mute (AlsaStreamControl *control, gboolean mute) +{ +    snd_mixer_elem_t *el; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    /* Set the switch for all channels */ +    ret = snd_mixer_selem_set_playback_switch_all (el, !mute); +    if (ret < 0) { +        g_warning ("Failed to set playback switch: %s", snd_strerror (ret)); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +alsa_stream_output_control_set_volume (AlsaStreamControl *control, guint volume) +{ +    snd_mixer_elem_t *el; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    /* Set the volume for all channels */ +    ret = snd_mixer_selem_set_playback_volume_all (el, volume); +    if (ret < 0) { +        g_warning ("Failed to set volume: %s", snd_strerror (ret)); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +alsa_stream_output_control_set_channel_volume (AlsaStreamControl           *control, +                                               snd_mixer_selem_channel_id_t channel, +                                               guint                        volume) +{ +    snd_mixer_elem_t *el; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    /* Set the volume for a single channels, the volume may still be "joined" and +     * set all the channels by itself */ +    ret = snd_mixer_selem_set_playback_volume (el, channel, volume); +    if (ret < 0) { +        g_warning ("Failed to set channel volume: %s", snd_strerror (ret)); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +alsa_stream_output_control_get_volume_from_decibel (AlsaStreamControl *control, +                                                    gdouble            decibel, +                                                    guint             *volume) +{ +    snd_mixer_elem_t *el; +    glong             value; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    ret = snd_mixer_selem_ask_playback_dB_vol (el, (glong) (decibel * 100), 0, &value); +    if (ret < 0) { +        g_warning ("Failed to convert volume: %s", snd_strerror (ret)); +        return FALSE; +    } + +    *volume = value; +    return TRUE; +} + +static gboolean +alsa_stream_output_control_get_decibel_from_volume (AlsaStreamControl *control, +                                                    guint              volume, +                                                    gdouble           *decibel) +{ +    snd_mixer_elem_t *el; +    glong             value; +    gint              ret; + +    g_return_val_if_fail (ALSA_IS_STREAM_CONTROL (control), FALSE); + +    el = alsa_element_get_snd_element (ALSA_ELEMENT (control)); +    if G_UNLIKELY (el == NULL) +        return FALSE; + +    ret = snd_mixer_selem_ask_playback_vol_dB (el, (glong) volume, &value); +    if (ret < 0) { +        g_warning ("Failed to convert volume: %s", snd_strerror (ret)); +        return FALSE; +    } + +    *decibel = value / 100.0; +    return TRUE; +} + +static void +read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data) +{ +    glong volume; +    glong min, max; +    gint  ret; +    gint  i; + +    /* Read volume ranges, this call should never fail on valid input */ +    ret = snd_mixer_selem_get_playback_volume_range (el, &min, &max); +    if G_UNLIKELY (ret < 0) { +        g_warning ("Failed to read playback volume range: %s", snd_strerror (ret)); +        return; +    } +    data->min = (guint) min; +    data->max = (guint) max; + +    /* This fails when decibels are not supported */ +    ret = snd_mixer_selem_get_playback_dB_range (el, &min, &max); +    if (ret == 0) { +        data->min_decibel = min / 100.0; +        data->max_decibel = max / 100.0; +    } else +        data->min_decibel = data->max_decibel = -MATE_MIXER_INFINITY; + +    for (i = 0; i < MATE_MIXER_CHANNEL_MAX; i++) +        data->v[i] = data->min; + +    data->volume = data->min; +    data->volume_joined = snd_mixer_selem_has_playback_volume_joined (el); + +    if (data->switch_usable == TRUE) +        data->switch_joined = snd_mixer_selem_has_playback_switch_joined (el); + +    if (snd_mixer_selem_is_playback_mono (el) == 1) { +        /* Special handling for single channel controls */ +        ret = snd_mixer_selem_get_playback_volume (el, SND_MIXER_SCHN_MONO, &volume); +        if (ret == 0) { +            data->channels = 1; + +            data->c[0] = MATE_MIXER_CHANNEL_MONO; +            data->v[0] = data->volume = (guint) volume; +        } else { +            g_warning ("Failed to read playback volume: %s", snd_strerror (ret)); +        } + +        if (data->switch_usable == TRUE) { +            gint value; + +            ret = snd_mixer_selem_get_playback_switch (el, SND_MIXER_SCHN_MONO, &value); +            if G_LIKELY (ret == 0) +                data->m[0] = !value; +        } +    } else { +        snd_mixer_selem_channel_id_t channel; + +        /* We use numeric channel indices, but ALSA only works with channel +         * positions, go over all the positions supported by ALSA and create +         * a list of channels */ +         for (channel = 0; channel < SND_MIXER_SCHN_LAST; channel++) { +            if (snd_mixer_selem_has_playback_channel (el, channel) == 0) +                continue; + +            if (data->switch_usable == TRUE) { +                gint value; + +                ret = snd_mixer_selem_get_playback_switch (el, channel, &value); +                if (ret == 0) +                    data->m[channel] = !value; +            } + +            ret = snd_mixer_selem_get_playback_volume (el, channel, &volume); +            if (ret < 0) { +                g_warning ("Failed to read playback volume: %s", snd_strerror (ret)); +                continue; +            } +            data->channels++; + +            /* The single value volume is the highest channel volume */ +            if (data->volume < volume) +                data->volume = volume; + +            data->c[channel] = alsa_channel_map_from[channel]; +            data->v[channel] = (guint) volume; +        } +    } +} diff --git a/backends/alsa/alsa-stream-output-control.h b/backends/alsa/alsa-stream-output-control.h new file mode 100644 index 0000000..845eaae --- /dev/null +++ b/backends/alsa/alsa-stream-output-control.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_STREAM_OUTPUT_CONTROL_H +#define ALSA_STREAM_OUTPUT_CONTROL_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "alsa-stream-control.h" + +G_BEGIN_DECLS + +#define ALSA_TYPE_STREAM_OUTPUT_CONTROL         \ +        (alsa_stream_output_control_get_type ()) +#define ALSA_STREAM_OUTPUT_CONTROL(o)           \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_STREAM_OUTPUT_CONTROL, AlsaStreamOutputControl)) +#define ALSA_IS_STREAM_OUTPUT_CONTROL(o)        \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_STREAM_OUTPUT_CONTROL)) +#define ALSA_STREAM_OUTPUT_CONTROL_CLASS(k)     \ +        (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_STREAM_OUTPUT_CONTROL, AlsaStreamOutputControlClass)) +#define ALSA_IS_STREAM_OUTPUT_CONTROL_CLASS(k)  \ +        (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_STREAM_OUTPUT_CONTROL)) +#define ALSA_STREAM_OUTPUT_CONTROL_GET_CLASS(o) \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_STREAM_OUTPUT_CONTROL, AlsaStreamOutputControlClass)) + +typedef struct _AlsaStreamOutputControl         AlsaStreamOutputControl; +typedef struct _AlsaStreamOutputControlClass    AlsaStreamOutputControlClass; +typedef struct _AlsaStreamOutputControlPrivate  AlsaStreamOutputControlPrivate; + +struct _AlsaStreamOutputControl +{ +    AlsaStreamControl parent; +}; + +struct _AlsaStreamOutputControlClass +{ +    AlsaStreamControlClass parent_class; +}; + +GType              alsa_stream_output_control_get_type (void) G_GNUC_CONST; + +AlsaStreamControl *alsa_stream_output_control_new      (const gchar               *name, +                                                        const gchar               *label, +                                                        MateMixerStreamControlRole role); + +G_END_DECLS + +#endif /* ALSA_STREAM_OUTPUT_CONTROL_H */ diff --git a/backends/alsa/alsa-stream.c b/backends/alsa/alsa-stream.c new file mode 100644 index 0000000..d2f68d4 --- /dev/null +++ b/backends/alsa/alsa-stream.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "alsa-element.h" +#include "alsa-stream.h" +#include "alsa-stream-control.h" +#include "alsa-switch.h" + +struct _AlsaStreamPrivate +{ +    GHashTable             *switches; +    GHashTable             *controls; +    MateMixerStreamControl *control; +}; + +static void alsa_stream_class_init (AlsaStreamClass *klass); +static void alsa_stream_init       (AlsaStream      *stream); +static void alsa_stream_dispose    (GObject         *object); +static void alsa_stream_finalize   (GObject         *object); + +G_DEFINE_TYPE (AlsaStream, alsa_stream, MATE_MIXER_TYPE_STREAM) + +static MateMixerStreamControl *alsa_stream_get_control         (MateMixerStream *mms, +                                                                const gchar     *name); +static MateMixerStreamControl *alsa_stream_get_default_control (MateMixerStream *mms); + +static MateMixerSwitch *       alsa_stream_get_switch          (MateMixerStream *mms, +                                                                const gchar     *name); + +static GList *                 alsa_stream_list_controls       (MateMixerStream *mms); +static GList *                 alsa_stream_list_switches       (MateMixerStream *mms); + +static void +alsa_stream_class_init (AlsaStreamClass *klass) +{ +    GObjectClass         *object_class; +    MateMixerStreamClass *stream_class; + +    object_class = G_OBJECT_CLASS (klass); +    object_class->dispose  = alsa_stream_dispose; +    object_class->finalize = alsa_stream_finalize; + +    stream_class = MATE_MIXER_STREAM_CLASS (klass); +    stream_class->get_control         = alsa_stream_get_control; +    stream_class->get_default_control = alsa_stream_get_default_control; +    stream_class->get_switch          = alsa_stream_get_switch; +    stream_class->list_controls       = alsa_stream_list_controls; +    stream_class->list_switches       = alsa_stream_list_switches; + +    g_type_class_add_private (object_class, sizeof (AlsaStreamPrivate)); +} + +static void +alsa_stream_init (AlsaStream *stream) +{ +    stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, +                                                ALSA_TYPE_STREAM, +                                                AlsaStreamPrivate); + +    stream->priv->controls = g_hash_table_new_full (g_str_hash, +                                                    g_str_equal, +                                                    g_free, +                                                    g_object_unref); + +    stream->priv->switches = g_hash_table_new_full (g_str_hash, +                                                    g_str_equal, +                                                    g_free, +                                                    g_object_unref); +} + +static void +alsa_stream_dispose (GObject *object) +{ +    AlsaStream *stream; + +    stream = ALSA_STREAM (object); + +    g_hash_table_remove_all (stream->priv->controls); +    g_hash_table_remove_all (stream->priv->switches); + +    g_clear_object (&stream->priv->control); + +    G_OBJECT_CLASS (alsa_stream_parent_class)->dispose (object); +} + +static void +alsa_stream_finalize (GObject *object) +{ +    AlsaStream *stream; + +    stream = ALSA_STREAM (object); + +    g_hash_table_destroy (stream->priv->controls); +    g_hash_table_destroy (stream->priv->switches); + +    G_OBJECT_CLASS (alsa_stream_parent_class)->finalize (object); +} + +AlsaStream * +alsa_stream_new (const gchar         *name, +                 MateMixerDevice     *device, +                 MateMixerStreamFlags flags) +{ +    return g_object_new (ALSA_TYPE_STREAM, +                         "name", name, +                         "device", device, +                         "flags", flags, +                         NULL); +} + +void +alsa_stream_add_control (AlsaStream *stream, AlsaStreamControl *control) +{ +    const gchar *name; + +    g_return_if_fail (ALSA_IS_STREAM (stream)); +    g_return_if_fail (ALSA_IS_STREAM_CONTROL (control)); + +    name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control)); +    g_hash_table_insert (stream->priv->controls, +                         g_strdup (name), +                         g_object_ref (control)); +} + +void +alsa_stream_add_switch (AlsaStream *stream, AlsaSwitch *swtch) +{ +    const gchar *name; + +    g_return_if_fail (ALSA_IS_STREAM (stream)); +    g_return_if_fail (ALSA_IS_SWITCH (swtch)); + +    name = mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch)); +    g_hash_table_insert (stream->priv->switches, +                         g_strdup (name), +                         g_object_ref (swtch)); +} + +gboolean +alsa_stream_is_empty (AlsaStream *stream) +{ +    g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE); + +    if (g_hash_table_size (stream->priv->controls) > 0 || +        g_hash_table_size (stream->priv->switches) > 0) +        return FALSE; + +    return TRUE; +} + +void +alsa_stream_set_default_control (AlsaStream *stream, AlsaStreamControl *control) +{ +    g_return_if_fail (ALSA_IS_STREAM (stream)); +    g_return_if_fail (ALSA_IS_STREAM_CONTROL (control)); + +    /* This function is only used internally so avoid validating that the control +     * belongs to this stream */ +    if (stream->priv->control != NULL) +        g_object_unref (stream->priv->control); + +    if (control != NULL) +        stream->priv->control = MATE_MIXER_STREAM_CONTROL (g_object_ref (control)); +    else +        stream->priv->control = NULL; +} + +void +alsa_stream_load_elements (AlsaStream *stream, const gchar *name) +{ +    AlsaElement *element; + +    g_return_if_fail (ALSA_IS_STREAM (stream)); +    g_return_if_fail (name != NULL); + +    element = g_hash_table_lookup (stream->priv->controls, name); +    if (element != NULL) +        alsa_element_load (element); + +    element = g_hash_table_lookup (stream->priv->switches, name); +    if (element != NULL) +        alsa_element_load (element); +} + +gboolean +alsa_stream_remove_elements (AlsaStream *stream, const gchar *name) +{ +    gboolean removed = FALSE; + +    g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE); +    g_return_val_if_fail (name != NULL, FALSE); + +    if (g_hash_table_remove (stream->priv->controls, name) == TRUE) +        removed = TRUE; +    if (g_hash_table_remove (stream->priv->switches, name) == TRUE) +        removed = TRUE; + +    return removed; +} + +static MateMixerStreamControl * +alsa_stream_get_control (MateMixerStream *mms, const gchar *name) +{ +    g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); + +    return g_hash_table_lookup (ALSA_STREAM (mms)->priv->controls, name); +} + +static MateMixerStreamControl * +alsa_stream_get_default_control (MateMixerStream *mms) +{ +    g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); + +    return ALSA_STREAM (mms)->priv->control; +} + +static MateMixerSwitch * +alsa_stream_get_switch (MateMixerStream *mms, const gchar *name) +{ +    g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); + +    return g_hash_table_lookup (ALSA_STREAM (mms)->priv->switches, name); +} + +static GList * +alsa_stream_list_controls (MateMixerStream *mms) +{ +    GList *list; + +    g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); + +    /* Convert the hash table to a linked list, this list is expected to be +     * cached in the main library */ +    list = g_hash_table_get_values (ALSA_STREAM (mms)->priv->controls); +    if (list != NULL) +        g_list_foreach (list, (GFunc) g_object_ref, NULL); + +    return list; +} + +static GList * +alsa_stream_list_switches (MateMixerStream *mms) +{ +    GList *list; + +    g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); + +    /* Convert the hash table to a linked list, this list is expected to be +     * cached in the main library */ +    list = g_hash_table_get_values (ALSA_STREAM (mms)->priv->switches); +    if (list != NULL) +        g_list_foreach (list, (GFunc) g_object_ref, NULL); + +    return list; +} diff --git a/backends/alsa/alsa-stream.h b/backends/alsa/alsa-stream.h new file mode 100644 index 0000000..f26a643 --- /dev/null +++ b/backends/alsa/alsa-stream.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_STREAM_H +#define ALSA_STREAM_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "alsa-element.h" +#include "alsa-stream-control.h" +#include "alsa-switch.h" + +G_BEGIN_DECLS + +#define ALSA_TYPE_STREAM                        \ +        (alsa_stream_get_type ()) +#define ALSA_STREAM(o)                          \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_STREAM, AlsaStream)) +#define ALSA_IS_STREAM(o)                       \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_STREAM)) +#define ALSA_STREAM_CLASS(k)                    \ +        (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_STREAM, AlsaStreamClass)) +#define ALSA_IS_STREAM_CLASS(k)                 \ +        (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_STREAM)) +#define ALSA_STREAM_GET_CLASS(o)                \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_STREAM, AlsaStreamClass)) + +typedef struct _AlsaStream         AlsaStream; +typedef struct _AlsaStreamClass    AlsaStreamClass; +typedef struct _AlsaStreamPrivate  AlsaStreamPrivate; + +struct _AlsaStream +{ +    MateMixerStream parent; + +    /*< private >*/ +    AlsaStreamPrivate *priv; +}; + +struct _AlsaStreamClass +{ +    MateMixerStreamClass parent_class; +}; + +GType       alsa_stream_get_type            (void) G_GNUC_CONST; + +AlsaStream *alsa_stream_new                 (const gchar         *name, +                                             MateMixerDevice     *device, +                                             MateMixerStreamFlags flags); + +void        alsa_stream_add_control         (AlsaStream          *stream, +                                             AlsaStreamControl   *control); + +void        alsa_stream_add_switch          (AlsaStream          *stream, +                                             AlsaSwitch          *swtch); + +gboolean    alsa_stream_is_empty            (AlsaStream          *stream); + +void        alsa_stream_set_default_control (AlsaStream          *stream, +                                             AlsaStreamControl   *control); + +void        alsa_stream_load_elements       (AlsaStream          *stream, +                                             const gchar         *name); + +gboolean    alsa_stream_remove_elements     (AlsaStream          *stream, +                                             const gchar         *name); + +void        alsa_stream_remove_all          (AlsaStream          *stream); + +G_END_DECLS + +#endif /* ALSA_STREAM_H */ diff --git a/backends/alsa/alsa-switch-option.c b/backends/alsa/alsa-switch-option.c new file mode 100644 index 0000000..2173113 --- /dev/null +++ b/backends/alsa/alsa-switch-option.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "alsa-switch-option.h" + +struct _AlsaSwitchOptionPrivate +{ +    guint id; +}; + +static void alsa_switch_option_class_init (AlsaSwitchOptionClass *klass); +static void alsa_switch_option_init       (AlsaSwitchOption      *option); + +G_DEFINE_TYPE (AlsaSwitchOption, alsa_switch_option, MATE_MIXER_TYPE_SWITCH_OPTION) + +static void +alsa_switch_option_class_init (AlsaSwitchOptionClass *klass) +{ +    g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (AlsaSwitchOptionPrivate)); +} + +static void +alsa_switch_option_init (AlsaSwitchOption *option) +{ +    option->priv = G_TYPE_INSTANCE_GET_PRIVATE (option, +                                                ALSA_TYPE_SWITCH_OPTION, +                                                AlsaSwitchOptionPrivate); +} + +AlsaSwitchOption * +alsa_switch_option_new (const gchar *name, +                        const gchar *label, +                        const gchar *icon, +                        guint        id) +{ +    AlsaSwitchOption *option; + +    option = g_object_new (ALSA_TYPE_SWITCH_OPTION, +                           "name", name, +                           "label", label, +                           NULL); + +    option->priv->id = id; +    return option; +} + +guint +alsa_switch_option_get_id (AlsaSwitchOption *option) +{ +    g_return_val_if_fail (ALSA_IS_SWITCH_OPTION (option), 0); + +    return option->priv->id; +} diff --git a/backends/alsa/alsa-switch-option.h b/backends/alsa/alsa-switch-option.h new file mode 100644 index 0000000..c2dda87 --- /dev/null +++ b/backends/alsa/alsa-switch-option.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_SWITCH_OPTION_H +#define ALSA_SWITCH_OPTION_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +G_BEGIN_DECLS + +#define ALSA_TYPE_SWITCH_OPTION                        \ +        (alsa_switch_option_get_type ()) +#define ALSA_SWITCH_OPTION(o)                          \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOption)) +#define ALSA_IS_SWITCH_OPTION(o)                       \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_SWITCH_OPTION)) +#define ALSA_SWITCH_OPTION_CLASS(k)                    \ +        (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass)) +#define ALSA_IS_SWITCH_OPTION_CLASS(k)                 \ +        (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_SWITCH_OPTION)) +#define ALSA_SWITCH_OPTION_GET_CLASS(o)                \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass)) + +typedef struct _AlsaSwitchOption         AlsaSwitchOption; +typedef struct _AlsaSwitchOptionClass    AlsaSwitchOptionClass; +typedef struct _AlsaSwitchOptionPrivate  AlsaSwitchOptionPrivate; + +struct _AlsaSwitchOption +{ +    MateMixerSwitchOption parent; + +    /*< private >*/ +    AlsaSwitchOptionPrivate *priv; +}; + +struct _AlsaSwitchOptionClass +{ +    MateMixerSwitchOptionClass parent_class; +}; + +GType             alsa_switch_option_get_type (void) G_GNUC_CONST; + +AlsaSwitchOption *alsa_switch_option_new      (const gchar      *name, +                                               const gchar      *label, +                                               const gchar      *icon, +                                               guint             id); + +guint             alsa_switch_option_get_id   (AlsaSwitchOption *option); + +G_END_DECLS + +#endif /* ALSA_SWITCH_OPTION_H */ diff --git a/backends/alsa/alsa-switch.c b/backends/alsa/alsa-switch.c new file mode 100644 index 0000000..15151ae --- /dev/null +++ b/backends/alsa/alsa-switch.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "alsa-element.h" +#include "alsa-switch.h" +#include "alsa-switch-option.h" + +struct _AlsaSwitchPrivate +{ +    GList            *options; +    guint32           channel_mask; +    snd_mixer_elem_t *element; +}; + +static void alsa_element_interface_init (AlsaElementInterface *iface); + +static void alsa_switch_class_init      (AlsaSwitchClass      *klass); +static void alsa_switch_init            (AlsaSwitch           *swtch); + +G_DEFINE_TYPE_WITH_CODE (AlsaSwitch, alsa_switch, +                         MATE_MIXER_TYPE_SWITCH, +                         G_IMPLEMENT_INTERFACE (ALSA_TYPE_ELEMENT, +                                                alsa_element_interface_init)) + +static gboolean               alsa_switch_set_active_option (MateMixerSwitch       *mms, +                                                             MateMixerSwitchOption *mmso); + +static GList *                alsa_switch_list_options      (MateMixerSwitch       *mms); + +static snd_mixer_elem_t *     alsa_switch_get_snd_element   (AlsaElement           *element); +static void                   alsa_switch_set_snd_element   (AlsaElement           *element, +                                                             snd_mixer_elem_t      *el); +static gboolean               alsa_switch_load              (AlsaElement           *element); + +static void +alsa_element_interface_init (AlsaElementInterface *iface) +{ +    iface->get_snd_element = alsa_switch_get_snd_element; +    iface->set_snd_element = alsa_switch_set_snd_element; +    iface->load            = alsa_switch_load; +} + +static void +alsa_switch_class_init (AlsaSwitchClass *klass) +{ +    MateMixerSwitchClass *switch_class; + +    switch_class = MATE_MIXER_SWITCH_CLASS (klass); +    switch_class->set_active_option = alsa_switch_set_active_option; +    switch_class->list_options      = alsa_switch_list_options; + +    g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (AlsaSwitchPrivate)); +} + +static void +alsa_switch_init (AlsaSwitch *swtch) +{ +    swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch, +                                               ALSA_TYPE_SWITCH, +                                               AlsaSwitchPrivate); +} + +AlsaSwitch * +alsa_switch_new (const gchar *name, const gchar *label, GList *options) +{ +    AlsaSwitch *swtch; + +    swtch = g_object_new (ALSA_TYPE_SWITCH, +                          "name", name, +                          "label", label, +                          NULL); + +    /* Takes ownership of options */ +    swtch->priv->options = options; +    return swtch; +} + +static gboolean +alsa_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso) +{ +    AlsaSwitch                  *swtch; +    guint                        index; +    gboolean                     set_item = FALSE; +    snd_mixer_selem_channel_id_t channel; + +    g_return_val_if_fail (ALSA_IS_SWITCH (mms), FALSE); +    g_return_val_if_fail (ALSA_IS_SWITCH_OPTION (mmso), FALSE); + +    swtch = ALSA_SWITCH (mms); + +    /* The channel mask is created when reading the active option the first +     * time, so a successful load must be done before changing the option */ +    if G_UNLIKELY (swtch->priv->channel_mask == 0) { +        g_debug ("Not setting active switch option, channel mask unknown"); +        return FALSE; +    } + +    index = alsa_switch_option_get_id (ALSA_SWITCH_OPTION (mmso)); + +    for (channel = 0; channel < SND_MIXER_SCHN_LAST; channel++) { +        /* The option is set per-channel, make sure to set it only for channels +         * we successfully read the value from */ +        if (swtch->priv->channel_mask & (1 << channel)) { +            gint ret = snd_mixer_selem_set_enum_item (swtch->priv->element, +                                                      channel, +                                                      index); +            if (ret == 0) +                set_item = TRUE; +            else +                g_warning ("Failed to set active option of switch %s: %s", +                           snd_mixer_selem_get_name (swtch->priv->element), +                           snd_strerror (ret)); +        } +    } +    return set_item; +} + +static GList * +alsa_switch_list_options (MateMixerSwitch *swtch) +{ +    g_return_val_if_fail (ALSA_IS_SWITCH (swtch), NULL); + +    return ALSA_SWITCH (swtch)->priv->options; +} + +static snd_mixer_elem_t * +alsa_switch_get_snd_element (AlsaElement *element) +{ +    g_return_val_if_fail (ALSA_IS_SWITCH (element), NULL); + +    return ALSA_SWITCH (element)->priv->element; +} + +static void +alsa_switch_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el) +{ +    g_return_if_fail (ALSA_IS_SWITCH (element)); +    g_return_if_fail (el != NULL); + +    ALSA_SWITCH (element)->priv->element = el; +} + +static gboolean +alsa_switch_load (AlsaElement *element) +{ +    AlsaSwitch                  *swtch; +    GList                       *list; +    guint                        item; +    gint                         ret; +    snd_mixer_selem_channel_id_t c; + +    swtch = ALSA_SWITCH (element); + +    /* When reading the first time we try all the channels, otherwise only the +     * ones which returned success before */ +    if (swtch->priv->channel_mask == 0) { +        for (c = 0; c < SND_MIXER_SCHN_LAST; c++) { +            ret = snd_mixer_selem_get_enum_item (swtch->priv->element, c, &item); + +            /* The active enum option is set per-channel, so when reading it the +             * first time, create a mask of all channels for which we read the +             * value successfully */ +            if (ret == 0) +                swtch->priv->channel_mask |= 1 << c; +        } + +        /* The last ALSA call might have failed, but it doesn't matter if we have +         * a channel mask */ +        if (swtch->priv->channel_mask > 0) +            ret = 0; +    } else { +        for (c = 0; !(swtch->priv->channel_mask & (1 << c)); c++) +            ; + +        /* When not reading the mask, the first usable channel is enough, we don't +         * support per-channel selections anyway */ +        ret = snd_mixer_selem_get_enum_item (swtch->priv->element, c, &item); +    } + +    if (ret < 0) { +        g_warning ("Failed to read active option of switch %s: %s", +                   snd_mixer_selem_get_name (swtch->priv->element), +                   snd_strerror (ret)); +        return FALSE; +    } + +    list = swtch->priv->options; +    while (list != NULL) { +        AlsaSwitchOption *option = ALSA_SWITCH_OPTION (list->data); + +        /* Mark the selected option when we find it, ALSA indentifies them +         * by numeric indices */ +        if (alsa_switch_option_get_id (option) == item) { +            _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch), +                                                  MATE_MIXER_SWITCH_OPTION (option)); +            return TRUE; +        } +        list = list->next; +    } + +    g_warning ("Unknown active option of switch %s: %d", +               snd_mixer_selem_get_name (swtch->priv->element), +               item); + +    return FALSE; +} diff --git a/backends/alsa/alsa-switch.h b/backends/alsa/alsa-switch.h new file mode 100644 index 0000000..fdcfb87 --- /dev/null +++ b/backends/alsa/alsa-switch.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_SWITCH_H +#define ALSA_SWITCH_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +G_BEGIN_DECLS + +#define ALSA_TYPE_SWITCH                        \ +        (alsa_switch_get_type ()) +#define ALSA_SWITCH(o)                          \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_SWITCH, AlsaSwitch)) +#define ALSA_IS_SWITCH(o)                       \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_SWITCH)) +#define ALSA_SWITCH_CLASS(k)                    \ +        (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_SWITCH, AlsaSwitchClass)) +#define ALSA_IS_SWITCH_CLASS(k)                 \ +        (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_SWITCH)) +#define ALSA_SWITCH_GET_CLASS(o)                \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_SWITCH, AlsaSwitchClass)) + +typedef struct _AlsaSwitch         AlsaSwitch; +typedef struct _AlsaSwitchClass    AlsaSwitchClass; +typedef struct _AlsaSwitchPrivate  AlsaSwitchPrivate; + +struct _AlsaSwitch +{ +    MateMixerSwitch parent; + +    /*< private >*/ +    AlsaSwitchPrivate *priv; +}; + +struct _AlsaSwitchClass +{ +    MateMixerSwitchClass parent_class; +}; + +GType       alsa_switch_get_type (void) G_GNUC_CONST; + +AlsaSwitch *alsa_switch_new      (const gchar *name, +                                  const gchar *label, +                                  GList       *options); + +G_END_DECLS + +#endif /* ALSA_SWITCH_H */ diff --git a/backends/alsa/alsa-toggle.c b/backends/alsa/alsa-toggle.c new file mode 100644 index 0000000..efa3460 --- /dev/null +++ b/backends/alsa/alsa-toggle.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <alsa/asoundlib.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "alsa-element.h" +#include "alsa-switch-option.h" +#include "alsa-toggle.h" + +struct _AlsaTogglePrivate +{ +    AlsaToggleType    type; +    guint32           channel_mask; +    snd_mixer_elem_t *element; +}; + +static void alsa_element_interface_init (AlsaElementInterface *iface); + +static void alsa_toggle_class_init      (AlsaToggleClass      *klass); +static void alsa_toggle_init            (AlsaToggle           *toggle); + +G_DEFINE_TYPE_WITH_CODE (AlsaToggle, alsa_toggle, MATE_MIXER_TYPE_TOGGLE, +                         G_IMPLEMENT_INTERFACE (ALSA_TYPE_ELEMENT, +                                                alsa_element_interface_init)) + +static gboolean               alsa_toggle_set_active_option (MateMixerSwitch       *mms, +                                                             MateMixerSwitchOption *mmso); + +static snd_mixer_elem_t *     alsa_toggle_get_snd_element   (AlsaElement           *element); +static void                   alsa_toggle_set_snd_element   (AlsaElement           *element, +                                                             snd_mixer_elem_t      *el); +static gboolean               alsa_toggle_load              (AlsaElement           *element); + +static void +alsa_element_interface_init (AlsaElementInterface *iface) +{ +    iface->get_snd_element = alsa_toggle_get_snd_element; +    iface->set_snd_element = alsa_toggle_set_snd_element; +    iface->load            = alsa_toggle_load; +} + +static void +alsa_toggle_class_init (AlsaToggleClass *klass) +{ +    MateMixerSwitchClass *switch_class; + +    switch_class = MATE_MIXER_SWITCH_CLASS (klass); +    switch_class->set_active_option = alsa_toggle_set_active_option; + +    g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (AlsaTogglePrivate)); +} + +static void +alsa_toggle_init (AlsaToggle *toggle) +{ +    toggle->priv = G_TYPE_INSTANCE_GET_PRIVATE (toggle, +                                                ALSA_TYPE_TOGGLE, +                                                AlsaTogglePrivate); +} + +AlsaToggle * +alsa_toggle_new (const gchar      *name, +                 const gchar      *label, +                 AlsaToggleType    type, +                 AlsaSwitchOption *on, +                 AlsaSwitchOption *off) +{ +    AlsaToggle *toggle; + +    toggle = g_object_new (ALSA_TYPE_TOGGLE, +                           "name", name, +                           "label", label, +                           "state-option-on", on, +                           "state-option-off", off, +                           NULL); + +    toggle->priv->type = type; +    return toggle; +} + +static gboolean +alsa_toggle_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso) +{ +    AlsaToggle *toggle; +    gint        value; +    gint        ret; + +    g_return_val_if_fail (ALSA_IS_TOGGLE (mms), FALSE); +    g_return_val_if_fail (ALSA_IS_SWITCH_OPTION (mmso), FALSE); + +    toggle = ALSA_TOGGLE (mms); + +    /* For toggles the 0/1 value is stored as the switch option id */ +    value = alsa_switch_option_get_id (ALSA_SWITCH_OPTION (mmso)); +    if G_UNLIKELY (value != 0 && value != 1) { +        g_warn_if_reached (); +        return FALSE; +    } + +    if (toggle->priv->type == ALSA_TOGGLE_CAPTURE) +        ret = snd_mixer_selem_set_capture_switch_all (toggle->priv->element, value); +    else +        ret = snd_mixer_selem_set_playback_switch_all (toggle->priv->element, value); + +    if (ret < 0) { +        g_warning ("Failed to set value of toggle %s: %s", +                   snd_mixer_selem_get_name (toggle->priv->element), +                   snd_strerror (ret)); +        return FALSE; +    } + +    return TRUE; +} + +static snd_mixer_elem_t * +alsa_toggle_get_snd_element (AlsaElement *element) +{ +    g_return_val_if_fail (ALSA_IS_TOGGLE (element), NULL); + +    return ALSA_TOGGLE (element)->priv->element; +} + +static void +alsa_toggle_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el) +{ +    g_return_if_fail (ALSA_IS_TOGGLE (element)); +    g_return_if_fail (el != NULL); + +    ALSA_TOGGLE (element)->priv->element = el; +} + +static gboolean +alsa_toggle_load (AlsaElement *element) +{ +    AlsaToggle                  *toggle; +    gint                         value; +    gint                         ret; +    snd_mixer_selem_channel_id_t c; + +    toggle = ALSA_TOGGLE (element); + +    /* When reading the first time we try all the channels, otherwise only the +     * ones which returned success before */ +    if (toggle->priv->channel_mask == 0) { +        for (c = 0; c < SND_MIXER_SCHN_LAST; c++) { +            if (toggle->priv->type == ALSA_TOGGLE_CAPTURE) +                ret = snd_mixer_selem_get_capture_switch (toggle->priv->element, +                                                          c, +                                                          &value); +            else +                ret = snd_mixer_selem_get_playback_switch (toggle->priv->element, +                                                           c, +                                                           &value); + +            /* The active enum option is set per-channel, so when reading it the +             * first time, create a mask of all channels for which we read the +             * value successfully */ +            if (ret == 0) +                toggle->priv->channel_mask |= 1 << c; +        } + +        /* The last ALSA call might have failed, but it doesn't matter if we have +         * a channel mask */ +        if (toggle->priv->channel_mask > 0) +            ret = 0; +    } else { +        for (c = 0; !(toggle->priv->channel_mask & (1 << c)); c++) +            ; + +        /* When not reading the mask, the first usable channel is enough, we don't +         * support per-channel selections anyway */ +        if (toggle->priv->type == ALSA_TOGGLE_CAPTURE) +            ret = snd_mixer_selem_get_capture_switch (toggle->priv->element, +                                                      c, +                                                      &value); +        else +            ret = snd_mixer_selem_get_playback_switch (toggle->priv->element, +                                                       c, +                                                       &value); +    } + +    if (ret == 0) { +        MateMixerSwitchOption *active; + +        if (value > 0) +            active = mate_mixer_toggle_get_state_option (MATE_MIXER_TOGGLE (toggle), TRUE); +        else +            active = mate_mixer_toggle_get_state_option (MATE_MIXER_TOGGLE (toggle), FALSE); + +        _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (toggle), active); + +        return TRUE; +    } + +    g_warning ("Failed to read state of toggle %s: %s", +               snd_mixer_selem_get_name (toggle->priv->element), +               snd_strerror (ret)); + +    return FALSE; +} diff --git a/backends/alsa/alsa-toggle.h b/backends/alsa/alsa-toggle.h new file mode 100644 index 0000000..d9c083b --- /dev/null +++ b/backends/alsa/alsa-toggle.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALSA_TOGGLE_H +#define ALSA_TOGGLE_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "alsa-switch-option.h" + +G_BEGIN_DECLS + +typedef enum { +    ALSA_TOGGLE_CAPTURE, +    ALSA_TOGGLE_PLAYBACK +} AlsaToggleType; + +#define ALSA_TYPE_TOGGLE                        \ +        (alsa_toggle_get_type ()) +#define ALSA_TOGGLE(o)                          \ +        (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_TOGGLE, AlsaToggle)) +#define ALSA_IS_TOGGLE(o)                       \ +        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_TOGGLE)) +#define ALSA_TOGGLE_CLASS(k)                    \ +        (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_TOGGLE, AlsaToggleClass)) +#define ALSA_IS_TOGGLE_CLASS(k)                 \ +        (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_TOGGLE)) +#define ALSA_TOGGLE_GET_CLASS(o)                \ +        (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_TOGGLE, AlsaToggleClass)) + +typedef struct _AlsaToggle         AlsaToggle; +typedef struct _AlsaToggleClass    AlsaToggleClass; +typedef struct _AlsaTogglePrivate  AlsaTogglePrivate; + +struct _AlsaToggle +{ +    MateMixerToggle parent; + +    /*< private >*/ +    AlsaTogglePrivate *priv; +}; + +struct _AlsaToggleClass +{ +    MateMixerToggleClass parent_class; +}; + +GType       alsa_toggle_get_type (void) G_GNUC_CONST; + +AlsaToggle *alsa_toggle_new      (const gchar      *name, +                                  const gchar      *label, +                                  AlsaToggleType    type, +                                  AlsaSwitchOption *on, +                                  AlsaSwitchOption *off); + +G_END_DECLS + +#endif /* ALSA_TOGGLE_H */ diff --git a/backends/oss/oss-backend.c b/backends/oss/oss-backend.c index 6e058f8..2b5eca7 100644 --- a/backends/oss/oss-backend.c +++ b/backends/oss/oss-backend.c @@ -19,91 +19,78 @@  #include <string.h>  #include <errno.h>  #include <glib.h> -#include <glib-object.h>  #include <glib/gstdio.h> -#include <glib/gprintf.h>  #include <glib/gi18n.h> -#include <gio/gio.h> +#include <glib-object.h> -#include <libmatemixer/matemixer-backend.h> -#include <libmatemixer/matemixer-backend-module.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h>  #include "oss-backend.h"  #include "oss-common.h"  #include "oss-device.h" +#include "oss-stream.h"  #define BACKEND_NAME      "OSS"  #define BACKEND_PRIORITY  9 -#define PATH_SNDSTAT      "/dev/sndstat" +#if !defined(__linux__) && !defined(__NetBSD__) && !defined(__OpenBSD__) +    /* At least on systems based on FreeBSD we will need to read device names +     * from the sndstat file, but avoid even trying that on systems where this +     * is not needed and the file is not present */ +#define OSS_PATH_SNDSTAT  "/dev/sndstat" +#endif + +#define OSS_MAX_DEVICES   32  struct _OssBackendPrivate  { -    GFile           *sndstat; -    GFile           *dev; -    GFileMonitor    *dev_monitor; -    GHashTable      *devices; -    GHashTable      *streams; -    MateMixerStream *default_input; -    MateMixerStream *default_output; -    MateMixerState   state; -}; - -enum { -    PROP_0, -    PROP_STATE, -    PROP_DEFAULT_INPUT, -    PROP_DEFAULT_OUTPUT +    gchar      *default_device; +    GSource    *timeout_source; +    GHashTable *devices;  }; -static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); -  static void oss_backend_class_init     (OssBackendClass *klass);  static void oss_backend_class_finalize (OssBackendClass *klass); - -static void oss_backend_get_property   (GObject         *object, -                                        guint            param_id, -                                        GValue          *value, -                                        GParamSpec      *pspec); -  static void oss_backend_init           (OssBackend      *oss);  static void oss_backend_dispose        (GObject         *object);  static void oss_backend_finalize       (GObject         *object); -G_DEFINE_DYNAMIC_TYPE_EXTENDED (OssBackend, oss_backend, -                                G_TYPE_OBJECT, 0, -                                G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, -                                                               mate_mixer_backend_interface_init)) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_DYNAMIC_TYPE (OssBackend, oss_backend, MATE_MIXER_TYPE_BACKEND) +#pragma clang diagnostic pop + +static gboolean oss_backend_open             (MateMixerBackend *backend); +static void     oss_backend_close            (MateMixerBackend *backend); +static GList *  oss_backend_list_devices     (MateMixerBackend *backend); +static GList *  oss_backend_list_streams     (MateMixerBackend *backend); -static gboolean oss_backend_open         (MateMixerBackend  *backend); -static void     oss_backend_close        (MateMixerBackend  *backend); -static GList *  oss_backend_list_devices (MateMixerBackend  *backend); -static GList *  oss_backend_list_streams (MateMixerBackend  *backend); +static gboolean read_devices                 (OssBackend       *oss); -static void     change_state                    (OssBackend        *oss, -                                                 MateMixerState     state); +static gboolean read_device                  (OssBackend       *oss, +                                              const gchar      *path, +                                              gboolean         *added); -static void     on_dev_monitor_changed          (GFileMonitor      *monitor, -                                                 GFile             *file, -                                                 GFile             *other_file, -                                                 GFileMonitorEvent  event_type, -                                                 OssBackend        *oss); +static gchar *  read_device_label            (OssBackend       *oss, +                                              const gchar      *path, +                                              gint              fd); -static gboolean read_device                     (OssBackend        *oss, -                                                 const gchar        *path); +static gchar *  read_device_label_sndstat    (OssBackend       *oss, +                                              const gchar      *sndstat, +                                              const gchar      *path, +                                              guint             index) G_GNUC_UNUSED; -static gchar *  read_device_description         (OssBackend        *oss, -                                                 const gchar       *path, -                                                 gint               fd); -static gchar *  read_device_sndstat_description (const gchar       *path, -                                                 GFileInputStream  *input); +static void     add_device                   (OssBackend       *oss, +                                              OssDevice        *device); +static void     remove_device                (OssBackend       *oss, +                                              OssDevice        *device); -static void     add_device                      (OssBackend        *oss, -                                                 OssDevice         *device); -static void     remove_device                   (OssBackend        *oss, -                                                 OssDevice         *device); +static void     remove_stream                (OssBackend       *oss, +                                              const gchar      *name); + +static void     select_default_input_stream  (OssBackend       *oss); +static void     select_default_output_stream (OssBackend       *oss);  static MateMixerBackendInfo info; @@ -124,65 +111,31 @@ const MateMixerBackendInfo *backend_module_get_info (void)  }  static void -mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) -{ -    iface->open         = oss_backend_open; -    iface->close        = oss_backend_close; -    iface->list_devices = oss_backend_list_devices; -    iface->list_streams = oss_backend_list_streams; -} - -static void  oss_backend_class_init (OssBackendClass *klass)  { -    GObjectClass *object_class; +    GObjectClass          *object_class; +    MateMixerBackendClass *backend_class;      object_class = G_OBJECT_CLASS (klass); -    object_class->dispose      = oss_backend_dispose; -    object_class->finalize     = oss_backend_finalize; -    object_class->get_property = oss_backend_get_property; +    object_class->dispose  = oss_backend_dispose; +    object_class->finalize = oss_backend_finalize; -    g_object_class_override_property (object_class, PROP_STATE, "state"); -    g_object_class_override_property (object_class, PROP_DEFAULT_INPUT, "default-input"); -    g_object_class_override_property (object_class, PROP_DEFAULT_OUTPUT, "default-output"); +    backend_class = MATE_MIXER_BACKEND_CLASS (klass); +    backend_class->open         = oss_backend_open; +    backend_class->close        = oss_backend_close; +    backend_class->list_devices = oss_backend_list_devices; +    backend_class->list_streams = oss_backend_list_streams;      g_type_class_add_private (object_class, sizeof (OssBackendPrivate));  } -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE() */  static void  oss_backend_class_finalize (OssBackendClass *klass)  {  }  static void -oss_backend_get_property (GObject    *object, -                          guint       param_id, -                          GValue     *value, -                          GParamSpec *pspec) -{ -    OssBackend *oss; - -    oss = OSS_BACKEND (object); - -    switch (param_id) { -    case PROP_STATE: -        g_value_set_enum (value, oss->priv->state); -        break; -    case PROP_DEFAULT_INPUT: -        g_value_set_object (value, oss->priv->default_input); -        break; -    case PROP_DEFAULT_OUTPUT: -        g_value_set_object (value, oss->priv->default_output); -        break; -        break; -    default: -        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); -        break; -    } -} - -static void  oss_backend_init (OssBackend *oss)  {      oss->priv = G_TYPE_INSTANCE_GET_PRIVATE (oss, @@ -193,17 +146,21 @@ oss_backend_init (OssBackend *oss)                                                  g_str_equal,                                                  g_free,                                                  g_object_unref); - -    oss->priv->streams = g_hash_table_new_full (g_str_hash, -                                                g_str_equal, -                                                g_free, -                                                g_object_unref);  }  static void  oss_backend_dispose (GObject *object)  { -    oss_backend_close (MATE_MIXER_BACKEND (object)); +    MateMixerBackend *backend; +    MateMixerState    state; + +    backend = MATE_MIXER_BACKEND (object); + +    state = mate_mixer_backend_get_state (backend); +    if (state != MATE_MIXER_STATE_IDLE) +        oss_backend_close (backend); + +    G_OBJECT_CLASS (oss_backend_parent_class)->dispose (object);  }  static void @@ -214,61 +171,34 @@ oss_backend_finalize (GObject *object)      oss = OSS_BACKEND (object);      g_hash_table_destroy (oss->priv->devices); -    g_hash_table_destroy (oss->priv->streams); + +    G_OBJECT_CLASS (oss_backend_parent_class)->finalize (object);  }  static gboolean  oss_backend_open (MateMixerBackend *backend)  {      OssBackend *oss; -    GError     *error = NULL; -    gint        i;      g_return_val_if_fail (OSS_IS_BACKEND (backend), FALSE);      oss = OSS_BACKEND (backend); -    /* Monitor changes on /dev to catch hot-(un)plugged devices */ -    // XXX works on linux, doesn't on freebsd, what to do on netbsd/openbsd? -    oss->priv->dev = g_file_new_for_path ("/dev"); -    oss->priv->dev_monitor = g_file_monitor_directory (oss->priv->dev, -                                                       G_FILE_MONITOR_NONE, -                                                       NULL, -                                                       &error); -    if (oss->priv->dev_monitor != NULL) { -        g_signal_connect (G_OBJECT (oss->priv->dev_monitor), -                          "changed", -                          G_CALLBACK (on_dev_monitor_changed), -                          oss); -    } else { -        g_debug ("Failed to monitor /dev: %s", error->message); -        g_error_free (error); -    } - -#if !defined(__linux__) && !defined(__NetBSD__) &&  !defined(__OpenBSD__) -    /* At least on systems based on FreeBSD we will need to read devices names -     * from the sndstat file, but avoid even trying that on systems where this -     * is not needed and the file is not present */ -    oss->priv->sndstat = g_file_new_for_path (PATH_SNDSTAT); -#endif - -    for (i = 0; i < 5; i++) { -        /* According to the OSS documentation the mixer devices are -         * /dev/mixer0 - /dev/mixer4, of course some systems create them -         * dynamically but this approach should be good enough */ -        gchar *path = g_strdup_printf ("/dev/mixer%i", i); - -        if (read_device (oss, path) == FALSE && i == 0) { -            /* For the first mixer device check also /dev/mixer, but it -             * might be a symlink to a real mixer device */ -            if (g_file_test ("/dev/mixer", G_FILE_TEST_IS_SYMLINK) == FALSE) -                read_device (oss, "/dev/mixer"); -        } - -        g_free (path); -    } - -    change_state (oss, MATE_MIXER_STATE_READY); +    /* Discover added or removed OSS devices every second */ +    oss->priv->timeout_source = g_timeout_source_new_seconds (1); +    g_source_set_callback (oss->priv->timeout_source, +                           (GSourceFunc) read_devices, +                           oss, +                           NULL); +    g_source_attach (oss->priv->timeout_source, +                     g_main_context_get_thread_default ()); + +    /* Read the initial list of devices so we have some starting point, there +     * isn't really a way to detect errors here, failing to add a device may +     * be a device-related problem so make the backend always open successfully */ +    read_devices (oss); + +    _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_READY);      return TRUE;  } @@ -281,15 +211,13 @@ oss_backend_close (MateMixerBackend *backend)      oss = OSS_BACKEND (backend); -    g_clear_object (&oss->priv->default_input); -    g_clear_object (&oss->priv->default_output); - -    g_hash_table_remove_all (oss->priv->streams); +    g_source_destroy (oss->priv->timeout_source);      g_hash_table_remove_all (oss->priv->devices); -    g_clear_object (&oss->priv->dev_monitor); -    g_clear_object (&oss->priv->dev); -    g_clear_object (&oss->priv->sndstat); +    g_free (oss->priv->default_device); +    oss->priv->default_device = NULL; + +    _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE);  }  static GList * @@ -299,306 +227,361 @@ oss_backend_list_devices (MateMixerBackend *backend)      g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL); -    /* Convert the hash table to a sorted linked list, this list is expected -     * to be cached in the main library */ +    /* Convert the hash table to a linked list, this list is expected to be +     * cached in the main library */      list = g_hash_table_get_values (OSS_BACKEND (backend)->priv->devices); -    if (list != NULL) { +    if (list != NULL)          g_list_foreach (list, (GFunc) g_object_ref, NULL); -        return list; -    } -    return NULL; +    return list;  }  static GList *  oss_backend_list_streams (MateMixerBackend *backend)  { -    GList *list; +    OssBackend    *oss; +    GHashTableIter iter; +    gpointer       value; +    GList         *list = NULL;      g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL); -    /* Convert the hash table to a sorted linked list, this list is expected -     * to be cached in the main library */ -    list = g_hash_table_get_values (OSS_BACKEND (backend)->priv->streams); -    if (list != NULL) { -        g_list_foreach (list, (GFunc) g_object_ref, NULL); +    oss = OSS_BACKEND (backend); -        return list; +    /* We don't keep a list or hash table of all streams here, instead walk +     * through the list of devices and create the list manually, each device +     * has at most one input and one output stream */ +    g_hash_table_iter_init (&iter, oss->priv->devices); + +    while (g_hash_table_iter_next (&iter, NULL, &value)) { +        OssDevice *device = OSS_DEVICE (value); +        OssStream *stream; + +        stream = oss_device_get_output_stream (device); +        if (stream != NULL) +            list = g_list_prepend (list, stream); +        stream = oss_device_get_input_stream (device); +        if (stream != NULL) +            list = g_list_prepend (list, stream);      } -    return NULL; -} - -static void -change_state (OssBackend *oss, MateMixerState state) -{ -    if (oss->priv->state == state) -        return; -    oss->priv->state = state; +    if (list != NULL) +        g_list_foreach (list, (GFunc) g_object_ref, NULL); -    g_object_notify (G_OBJECT (oss), "state"); +    return list;  } -static void -on_dev_monitor_changed (GFileMonitor     *monitor, -                        GFile            *file, -                        GFile            *other_file, -                        GFileMonitorEvent event_type, -                        OssBackend       *oss) +static gboolean +read_devices (OssBackend *oss)  { -    gchar *path; +    gint     i; +    gboolean added = FALSE; -    if (event_type != G_FILE_MONITOR_EVENT_CREATED && -        event_type != G_FILE_MONITOR_EVENT_DELETED) -        return; +    for (i = 0; i < OSS_MAX_DEVICES; i++) { +        gboolean added_current; +        gchar   *path = g_strdup_printf ("/dev/mixer%i", i); -    path = g_file_get_path (file); +        /* On recent FreeBSD both /dev/mixer and /dev/mixer0 point to the same +         * mixer device, on NetBSD and OpenBSD /dev/mixer is a symlink to one +         * of the real mixer device nodes, on Linux /dev/mixer is the first +         * device and /dev/mixer1 is the second device. +         * Handle all of these cases by trying /dev/mixer if /dev/mixer0 fails */ +        if (read_device (oss, path, &added_current) == FALSE && i == 0) +            read_device (oss, "/dev/mixer", &added_current); + +        if (added_current) +            added = TRUE; -    /* Only handle creation and deletion of mixer devices */ -    if (g_str_has_prefix (path, "/dev/mixer") == FALSE) { -        g_free (path); -        return; -    } -    if (strcmp (path, "/dev/mixer") == 0 && -        g_file_test (path, G_FILE_TEST_IS_SYMLINK) == TRUE) {          g_free (path); -        return;      } -    if (event_type == G_FILE_MONITOR_EVENT_DELETED) { -        OssDevice *device; - -        device = g_hash_table_lookup (oss->priv->devices, path); -        if (device != NULL) -            remove_device (oss, device); -    } else -        read_device (oss, path); - -    g_free (path); +    /* If any card has been added, make sure we have the most suitable default +     * input and output streams */ +    if (added == TRUE) { +        select_default_input_stream (oss); +        select_default_output_stream (oss); +    } +    return G_SOURCE_CONTINUE;  }  static gboolean -read_device (OssBackend *oss, const gchar *path) +read_device (OssBackend *oss, const gchar *path, gboolean *added)  { -    OssDevice   *device; -    gint         fd; -    gboolean     ret; -    const gchar *description; +    OssDevice *device; +    gint       fd; +    gchar     *bname; +    gchar     *label; -    /* We assume that the name and capabilities of a device do not change */      device = g_hash_table_lookup (oss->priv->devices, path); -    if (G_UNLIKELY (device != NULL)) { -        g_debug ("Attempt to re-read already know device %s", path); -        return TRUE; -    } +    *added = FALSE;      fd = g_open (path, O_RDWR, 0);      if (fd == -1) {          if (errno != ENOENT && errno != ENXIO)              g_debug ("%s: %s", path, g_strerror (errno)); + +        if (device != NULL) +            remove_device (oss, device);          return FALSE;      } -    device = oss_device_new (path, fd); +    /* Don't proceed if the device is already known, opening the device was +     * still tested to be absolutely sure that the device is removed it case +     * it has disappeared, but normally the device's polling facility should +     * handle this by itself */ +    if (device != NULL) { +        close (fd); +        return TRUE; +    } -    description = read_device_description (oss, path, fd); -    if (description != NULL) -        oss_device_set_description (device, description); -    else -        oss_device_set_description (device, _("Unknown device")); +    bname = g_path_get_basename (path); +    label = read_device_label (oss, path, fd); -    /* Close the descriptor as the device should dup it if it intends -     * to keep it */ -    g_close (fd, NULL); +    device = oss_device_new (bname, label, path, fd); +    g_free (bname); +    g_free (label); -    ret = oss_device_read (device); -    if (ret == TRUE) +    close (fd); + +    if ((*added = oss_device_open (device)) == TRUE)          add_device (oss, device);      g_object_unref (device); -    return ret; +    return *added;  }  static gchar * -read_device_description (OssBackend *oss, const gchar *path, gint fd) +read_device_label (OssBackend *oss, const gchar *path, gint fd)  { -    struct mixer_info info; +    guint index; -    if (ioctl (fd, SOUND_MIXER_INFO, &info) == 0) { -        /* Prefer device name supplied by the system, but this fails on FreeBSD */ -        return g_strdup (info.name); -    } +#ifdef SOUND_MIXER_INFO +    do { +        struct mixer_info info; -    /* Reading the sndstat file is a bit desperate, but seem to be the only -     * way to determine the name of the sound card on FreeBSD, it also has an -     * advantage in being able to find out the default one */ -    if (oss->priv->sndstat != NULL) { -        GError           *error = NULL; -        GFileInputStream *input = g_file_read (oss->priv->sndstat, NULL, &error); - -        if (input == NULL) { -            /* The file can only be open by one application, otherwise the call -             * fails with EBUSY */ -            if (error->code == G_IO_ERROR_BUSY) { -                g_debug ("Delayed reading %s as it is busy", PATH_SNDSTAT); -                // XXX use timeout and try again -            } else { -                if (error->code == G_IO_ERROR_NOT_FOUND) -                    g_debug ("Device description is unknown as %s does not exist", -                             PATH_SNDSTAT); -                else -                    g_debug ("%s: %s", PATH_SNDSTAT, error->message); +        /* Prefer device name supplied by the system, but this calls fails +         * with EINVAL on FreeBSD */ +        if (ioctl (fd, SOUND_MIXER_INFO, &info) == 0) +            return g_strdup (info.name); +    } while (0); +#endif -                g_clear_object (&oss->priv->sndstat); -            } -            g_error_free (error); -        } else -            return read_device_sndstat_description (path, input); -    } +    index = (guint) g_ascii_strtoull (path + sizeof ("/dev/mixer") - 1, +                                      NULL, +                                      10); +#ifdef OSS_PATH_SNDSTAT +    /* If the ioctl doesn't succeed, assume that the mixer device number +     * matches the pcm number in the sndstat file, this is a bit desperate, but +     * it should be correct on FreeBSD */ +    do { +        gchar *label; + +        label = read_device_label_sndstat (oss, OSS_PATH_SNDSTAT, path, index); +        if (label != NULL) +            return label; +    } while (0); +#endif -    return NULL; +    return g_strdup_printf (_("OSS Mixer %d"), index);  }  static gchar * -read_device_sndstat_description (const gchar *path, GFileInputStream *input) +read_device_label_sndstat (OssBackend  *oss, +                           const gchar *sndstat, +                           const gchar *path, +                           guint        index)  { -    gchar            *line; -    gchar            *prefix; -    gchar            *description = NULL; -    GDataInputStream *stream; - -    if (G_UNLIKELY (g_str_has_prefix (path, "/dev/mixer")) == FALSE) { -        g_warn_if_reached (); +    FILE  *fp; +    gchar *label = NULL; +    gchar *prefix; +    gchar  line[512]; + +    fp = g_fopen (sndstat, "r"); +    if (fp == NULL) { +        g_debug ("Failed to open %s: %s", sndstat, g_strerror (errno));          return NULL;      } -    /* We assume that the mixer device number matches the pcm number in the -     * sndstat file, this is a bit desperate, but it seems to do the -     * right thing in practice */ -    prefix = g_strdup_printf ("pcm%u: ", -                              (guint) g_ascii_strtoull (path + sizeof ("/dev/mixer") - 1, -                                                        NULL, -                                                        10)); - -    stream = g_data_input_stream_new (G_INPUT_STREAM (input)); - -    while (TRUE) { -        GError *error = NULL; -        gchar  *p; - -        line = g_data_input_stream_read_line (stream, NULL, NULL, &error); -        if (line == NULL) { -            if (error != NULL) { -                g_warning ("%s: %s", path, error->message); -                g_error_free (error); -            } -            break; -        } +    /* Example line: +     * pcm0: <ATI R6xx (HDMI)> (play) default */ +    prefix = g_strdup_printf ("pcm%u: ", index); + +    while (fgets (line, sizeof (line), fp) != NULL) { +        gchar *p;          if (g_str_has_prefix (line, prefix) == FALSE)              continue; -        /* Example line: -         * pcm0: <ATI R6xx (HDMI)> (play) default */          p = strchr (line, '<');          if (p != NULL && *p && *(++p)) {              gchar *end = strchr (p, '>'); -            if (end != NULL) -                description = g_strndup (p, end - p); -        } +            if (end != NULL) { +                label = g_strndup (p, end - p); -        // XXX we can also read "default" at the end of the line -        // XXX http://ashish.is.lostca.se/2011/05/23/default-sound-device-in-freebsd/ -        if (g_str_has_suffix (line, "default") || -            g_str_has_suffix (line, "default)")) -            ; +                /* Normally the default OSS device is /dev/dsp, but on FreeBSD +                 * /dev/dsp doesn't physically exist on the filesystem, but is +                 * managed by the kernel according to the user-settable default +                 * device, in sndstat the default card definition ends with the +                 * word "default" */ +                if (g_str_has_suffix (line, "default")) { +                    g_free (oss->priv->default_device); -        if (description != NULL) +                    oss->priv->default_device = g_strdup (path); +                } +            } else { +                g_debug ("Failed to read sndstat line: %s", line); +            }              break; +        }      } -    g_object_unref (stream);      g_free (prefix); +    fclose (fp); -    return description; +    return label;  }  static void  add_device (OssBackend *oss, OssDevice *device)  { -    MateMixerStream *stream; +    const gchar *name; + +    name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); -    /* Add device, file path is used as the key rather than the name, because -     * the name is not known until an OssDevice instance is created */      g_hash_table_insert (oss->priv->devices,                           g_strdup (oss_device_get_path (device)),                           g_object_ref (device)); -    g_signal_emit_by_name (G_OBJECT (oss), -                           "device-added", -                           mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); - -    /* Add streams if they exist */ -    stream = oss_device_get_input_stream (device); -    if (stream != NULL) { -        g_hash_table_insert (oss->priv->streams, -                             g_strdup (mate_mixer_stream_get_name (stream)), -                             g_object_ref (stream)); - -        g_signal_emit_by_name (G_OBJECT (oss), -                               "stream-added", -                               mate_mixer_stream_get_name (stream)); -    } +    // XXX make device emit it when closed +    g_signal_connect_swapped (G_OBJECT (device), +                              "stream-removed", +                              G_CALLBACK (remove_stream), +                              oss); -    stream = oss_device_get_output_stream (device); -    if (stream != NULL) { -        g_hash_table_insert (oss->priv->streams, -                             g_strdup (mate_mixer_stream_get_name (stream)), -                             g_object_ref (stream)); +    g_signal_emit_by_name (G_OBJECT (oss), "device-added", name); -        g_signal_emit_by_name (G_OBJECT (oss), -                               "stream-added", -                               mate_mixer_stream_get_name (stream)); -    } +    oss_device_load (device);  }  static void  remove_device (OssBackend *oss, OssDevice *device)  { +    const gchar *name; +    const gchar *path; + +    path = oss_device_get_path (device); +    name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + +    g_signal_handlers_disconnect_by_func (G_OBJECT (device), +                                          G_CALLBACK (remove_stream), +                                          oss); + +    // XXX close the device and make it remove streams +    g_hash_table_remove (oss->priv->devices, path); + +    g_signal_emit_by_name (G_OBJECT (oss), +                           "device-removed", +                           name); +} + +static void +remove_stream (OssBackend *oss, const gchar *name) +{      MateMixerStream *stream; -    const gchar     *path; -    /* Remove the device streams first as they are a part of the device */ -    stream = oss_device_get_input_stream (device); -    if (stream != NULL) { -        const gchar *name = mate_mixer_stream_get_name (stream); +    stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (oss)); + +    // XXX see if the change happens after stream is removed or before +    if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) +        select_default_input_stream (oss); + +    stream = mate_mixer_backend_get_default_output_stream (MATE_MIXER_BACKEND (oss)); + +    if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) +        select_default_output_stream (oss); +} -        g_hash_table_remove (oss->priv->streams, name); -        g_signal_emit_by_name (G_OBJECT (oss), -                               "stream-removed", -                               name); +static void +select_default_input_stream (OssBackend *oss) +{ +    OssDevice *device = NULL; +    OssStream *stream; +    gint       i; + +    /* Always prefer stream in the "default" device */ +    if (oss->priv->default_device != NULL) +        device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device); +    if (device != NULL) { +        stream = oss_device_get_input_stream (device); +        if (stream != NULL) { +            _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), +                                                          MATE_MIXER_STREAM (stream)); +            return; +        }      } -    stream = oss_device_get_output_stream (device); -    if (stream != NULL) { -        const gchar *name = mate_mixer_stream_get_name (stream); +    for (i = 0; i < OSS_MAX_DEVICES; i++) { +        gchar *path = g_strdup_printf ("/dev/mixer%i", i); -        g_hash_table_remove (oss->priv->streams, stream); -        g_signal_emit_by_name (G_OBJECT (oss), -                               "stream-removed", -                               name); +        device = g_hash_table_lookup (oss->priv->devices, path); +        if (device == NULL && i == 0) +            device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer"); + +        if (device != NULL) { +            stream = oss_device_get_input_stream (device); +            if (stream != NULL) { +                _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), +                                                              MATE_MIXER_STREAM (stream)); +                g_free (path); +                return; +            } +        } +        g_free (path);      } -    path = oss_device_get_path (device); +    /* In the worst case unset the default stream */ +    _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), NULL); +} -    /* Remove the device */ -    g_object_ref (device); +static void +select_default_output_stream (OssBackend *oss) +{ +    OssDevice *device = NULL; +    OssStream *stream; +    gint       i; + +    /* Always prefer stream in the "default" device */ +    if (oss->priv->default_device != NULL) +        device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device); +    if (device != NULL) { +        stream = oss_device_get_output_stream (device); +        if (stream != NULL) { +            _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), +                                                           MATE_MIXER_STREAM (stream)); +            return; +        } +    } -    g_hash_table_remove (oss->priv->devices, path); -    g_signal_emit_by_name (G_OBJECT (oss), -                           "device-removed", -                           mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); +    for (i = 0; i < OSS_MAX_DEVICES; i++) { +        gchar *path = g_strdup_printf ("/dev/mixer%i", i); -    g_object_unref (device); +        device = g_hash_table_lookup (oss->priv->devices, path); +        if (device == NULL && i == 0) +            device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer"); + +        if (device != NULL) { +            stream = oss_device_get_output_stream (device); +            if (stream != NULL) { +                _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), +                                                               MATE_MIXER_STREAM (stream)); +                g_free (path); +                return; +            } +        } +        g_free (path); +    } + +    /* In the worst case unset the default stream */ +    _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), NULL);  } diff --git a/backends/oss/oss-backend.h b/backends/oss/oss-backend.h index b766799..325b61c 100644 --- a/backends/oss/oss-backend.h +++ b/backends/oss/oss-backend.h @@ -21,6 +21,7 @@  #include <glib.h>  #include <glib-object.h> +#include <libmatemixer/matemixer-backend.h>  #include <libmatemixer/matemixer-backend-module.h>  #define OSS_TYPE_BACKEND                        \ @@ -42,7 +43,7 @@ typedef struct _OssBackendPrivate  OssBackendPrivate;  struct _OssBackend  { -    GObject parent; +    MateMixerBackend parent;      /*< private >*/      OssBackendPrivate *priv; @@ -50,7 +51,7 @@ struct _OssBackend  struct _OssBackendClass  { -    GObjectClass parent_class; +    MateMixerBackendClass parent_class;  };  GType                       oss_backend_get_type   (void) G_GNUC_CONST; diff --git a/backends/oss/oss-common.h b/backends/oss/oss-common.h index 7251b86..28a138d 100644 --- a/backends/oss/oss-common.h +++ b/backends/oss/oss-common.h @@ -24,6 +24,7 @@  #include <sys/stat.h>  #include <sys/ioctl.h>  #include <fcntl.h> +#include <unistd.h>  #ifdef HAVE_SYS_SOUNDCARD_H  #  include <sys/soundcard.h> diff --git a/backends/oss/oss-device.c b/backends/oss/oss-device.c index f33ff57..cf51705 100644 --- a/backends/oss/oss-device.c +++ b/backends/oss/oss-device.c @@ -22,12 +22,8 @@  #include <glib/gstdio.h>  #include <glib-object.h> -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-port-private.h> -#include <libmatemixer/matemixer-stream.h> -#include <libmatemixer/matemixer-stream-control.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h>  #include "oss-common.h"  #include "oss-device.h" @@ -36,490 +32,373 @@  #define OSS_DEVICE_ICON "audio-card" +typedef enum +{ +    OSS_DEV_ANY, +    OSS_DEV_INPUT, +    OSS_DEV_OUTPUT +} OssDevType; +  typedef struct  {      gchar                     *name; -    gchar                     *description; +    gchar                     *label;      MateMixerStreamControlRole role; -    MateMixerStreamFlags       flags; -} OssDeviceName; - -static const OssDeviceName const oss_devices[] = { -    { "vol",     N_("Volume"),     MATE_MIXER_STREAM_CONTROL_ROLE_MASTER,  MATE_MIXER_STREAM_OUTPUT }, -    { "bass",    N_("Bass"),       MATE_MIXER_STREAM_CONTROL_ROLE_BASS,    MATE_MIXER_STREAM_OUTPUT }, -    { "treble",  N_("Treble"),     MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE,  MATE_MIXER_STREAM_OUTPUT }, -    { "synth",   N_("Synth"),      MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, -    { "pcm",     N_("PCM"),        MATE_MIXER_STREAM_CONTROL_ROLE_PCM,     MATE_MIXER_STREAM_OUTPUT }, -    { "speaker", N_("Speaker"),    MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, MATE_MIXER_STREAM_OUTPUT }, -    { "line",    N_("Line-in"),    MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_INPUT }, -    { "mic",     N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_INPUT }, -    { "cd",      N_("CD"),         MATE_MIXER_STREAM_CONTROL_ROLE_CD,      MATE_MIXER_STREAM_INPUT }, +    OssDevType                 type; +} OssDev; + +static const OssDev oss_devices[] = { +    { "vol",     N_("Volume"),     MATE_MIXER_STREAM_CONTROL_ROLE_MASTER,  OSS_DEV_OUTPUT }, +    { "bass",    N_("Bass"),       MATE_MIXER_STREAM_CONTROL_ROLE_BASS,    OSS_DEV_OUTPUT }, +    { "treble",  N_("Treble"),     MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE,  OSS_DEV_OUTPUT }, +    { "synth",   N_("Synth"),      MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }, +    { "pcm",     N_("PCM"),        MATE_MIXER_STREAM_CONTROL_ROLE_PCM,     OSS_DEV_OUTPUT }, +    /* OSS manual says this should be the beeper, but Linux OSS seems to assign it to +     * regular volume control */ +    { "speaker", N_("Speaker"),    MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, OSS_DEV_OUTPUT }, +    { "line",    N_("Line-in"),    MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_INPUT }, +    { "mic",     N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_INPUT }, +    { "cd",      N_("CD"),         MATE_MIXER_STREAM_CONTROL_ROLE_CD,      OSS_DEV_INPUT },      /* Recording monitor */ -    { "mix",     N_("Mixer"),      MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, -    { "pcm2",    N_("PCM-2"),      MATE_MIXER_STREAM_CONTROL_ROLE_PCM,     MATE_MIXER_STREAM_OUTPUT }, +    { "mix",     N_("Mixer"),      MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, +    { "pcm2",    N_("PCM-2"),      MATE_MIXER_STREAM_CONTROL_ROLE_PCM,     OSS_DEV_OUTPUT },      /* Recording level (master input) */ -    { "rec",     N_("Record"),     MATE_MIXER_STREAM_CONTROL_ROLE_MASTER,  MATE_MIXER_STREAM_INPUT }, -    { "igain",   N_("In-gain"),    MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT }, -    { "ogain",   N_("Out-gain"),   MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, -    { "line1",   N_("Line-1"),     MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_INPUT }, -    { "line2",   N_("Line-2"),     MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_INPUT }, -    { "line3",   N_("Line-3"),     MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_INPUT }, -    { "dig1",    N_("Digital-1"),  MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_NO_FLAGS }, -    { "dig2",    N_("Digital-2"),  MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_NO_FLAGS }, -    { "dig3",    N_("Digital-3"),  MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_NO_FLAGS }, -    { "phin",    N_("Phone-in"),   MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_INPUT }, -    { "phout",   N_("Phone-out"),  MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_OUTPUT }, -    { "video",   N_("Video"),      MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_INPUT }, -    { "radio",   N_("Radio"),      MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    MATE_MIXER_STREAM_INPUT }, -    { "monitor", N_("Monitor"),    MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, -    { "depth",   N_("3D-depth"),   MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, -    { "center",  N_("3D-center"),  MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_OUTPUT }, -    { "midi",    N_("MIDI"),       MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_INPUT } +    { "rec",     N_("Record"),     MATE_MIXER_STREAM_CONTROL_ROLE_MASTER,  OSS_DEV_INPUT }, +    { "igain",   N_("In-gain"),    MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }, +    { "ogain",   N_("Out-gain"),   MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, +    { "line1",   N_("Line-1"),     MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_INPUT }, +    { "line2",   N_("Line-2"),     MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_INPUT }, +    { "line3",   N_("Line-3"),     MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_INPUT }, +    { "dig1",    N_("Digital-1"),  MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_ANY }, +    { "dig2",    N_("Digital-2"),  MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_ANY }, +    { "dig3",    N_("Digital-3"),  MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_ANY }, +    { "phin",    N_("Phone-in"),   MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_INPUT }, +    { "phout",   N_("Phone-out"),  MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_OUTPUT }, +    { "video",   N_("Video"),      MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_INPUT }, +    { "radio",   N_("Radio"),      MATE_MIXER_STREAM_CONTROL_ROLE_PORT,    OSS_DEV_INPUT }, +    { "monitor", N_("Monitor"),    MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, +    { "depth",   N_("3D-depth"),   MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, +    { "center",  N_("3D-center"),  MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, +    { "midi",    N_("MIDI"),       MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }  }; +#define OSS_N_DEVICES MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES) +  struct _OssDevicePrivate  { -    gint             fd; -    gchar           *path; -    gchar           *name; -    gchar           *description; -    MateMixerStream *input; -    MateMixerStream *output; -}; - -enum { -    PROP_0, -    PROP_NAME, -    PROP_DESCRIPTION, -    PROP_ICON, -    PROP_ACTIVE_PROFILE, -    PROP_PATH, -    PROP_FD +    gint       fd; +    gchar     *path; +    gint       devmask; +    gint       stereodevs; +    gint       recmask; +    gint       recsrc; +    OssStream *input; +    OssStream *output;  }; -static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); -  static void oss_device_class_init   (OssDeviceClass *klass); - -static void oss_device_get_property (GObject        *object, -                                     guint           param_id, -                                     GValue         *value, -                                     GParamSpec     *pspec); -static void oss_device_set_property (GObject        *object, -                                     guint           param_id, -                                     const GValue   *value, -                                     GParamSpec     *pspec); -  static void oss_device_init         (OssDevice      *device); +static void oss_device_dispose      (GObject        *object);  static void oss_device_finalize     (GObject        *object); -G_DEFINE_TYPE_WITH_CODE (OssDevice, oss_device, G_TYPE_OBJECT, -                         G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE, -                                                mate_mixer_device_interface_init)) +G_DEFINE_TYPE (OssDevice, oss_device, MATE_MIXER_TYPE_DEVICE) -static const gchar *oss_device_get_name        (MateMixerDevice *device); -static const gchar *oss_device_get_description (MateMixerDevice *device); -static const gchar *oss_device_get_icon        (MateMixerDevice *device); +static GList *  oss_device_list_streams    (MateMixerDevice  *device); -static gboolean     read_mixer_devices         (OssDevice       *device); +static gboolean read_mixer_devices         (OssDevice        *device); -static void -mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) -{ -    iface->get_name        = oss_device_get_name; -    iface->get_description = oss_device_get_description; -    iface->get_icon        = oss_device_get_icon; -} +static gboolean set_stream_default_control (OssStream        *stream, +                                            OssStreamControl *control, +                                            gboolean          force);  static void  oss_device_class_init (OssDeviceClass *klass)  { -    GObjectClass *object_class; +    GObjectClass         *object_class; +    MateMixerDeviceClass *device_class;      object_class = G_OBJECT_CLASS (klass); -    object_class->finalize     = oss_device_finalize; -    object_class->get_property = oss_device_get_property; -    object_class->set_property = oss_device_set_property; - -    g_object_class_install_property (object_class, -                                     PROP_PATH, -                                     g_param_spec_string ("path", -                                                          "Path", -                                                          "Path to the device", -                                                          NULL, -                                                          G_PARAM_CONSTRUCT_ONLY | -                                                          G_PARAM_READWRITE | -                                                          G_PARAM_STATIC_STRINGS)); - -    g_object_class_install_property (object_class, -                                     PROP_FD, -                                     g_param_spec_int ("fd", -                                                       "File descriptor", -                                                       "File descriptor of the device", -                                                       G_MININT, -                                                       G_MAXINT, -                                                       -1, -                                                       G_PARAM_CONSTRUCT_ONLY | -                                                       G_PARAM_READWRITE | -                                                       G_PARAM_STATIC_STRINGS)); - -    g_object_class_override_property (object_class, PROP_NAME, "name"); -    g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); -    g_object_class_override_property (object_class, PROP_ICON, "icon"); -    g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile"); +    object_class->dispose  = oss_device_dispose; +    object_class->finalize = oss_device_finalize; + +    device_class = MATE_MIXER_DEVICE_CLASS (klass); +    device_class->list_streams = oss_device_list_streams;      g_type_class_add_private (object_class, sizeof (OssDevicePrivate));  }  static void -oss_device_get_property (GObject    *object, -                         guint       param_id, -                         GValue     *value, -                         GParamSpec *pspec) +oss_device_init (OssDevice *device)  { -    OssDevice *device; - -    device = OSS_DEVICE (object); - -    switch (param_id) { -    case PROP_NAME: -        g_value_set_string (value, device->priv->name); -        break; -    case PROP_DESCRIPTION: -        g_value_set_string (value, device->priv->description); -        break; -    case PROP_ICON: -        g_value_set_string (value, OSS_DEVICE_ICON); -        break; -    case PROP_ACTIVE_PROFILE: -        /* Not supported */ -        g_value_set_object (value, NULL); -        break; -    case PROP_PATH: -        g_value_set_string (value, device->priv->path); -        break; -    case PROP_FD: -        g_value_set_int (value, device->priv->fd); -        break; -    default: -        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); -        break; -    } +    device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, +                                                OSS_TYPE_DEVICE, +                                                OssDevicePrivate);  }  static void -oss_device_set_property (GObject      *object, -                         guint         param_id, -                         const GValue *value, -                         GParamSpec   *pspec) +oss_device_dispose (GObject *object)  {      OssDevice *device;      device = OSS_DEVICE (object); -    switch (param_id) { -    case PROP_PATH: -        /* Construct-only string */ -        device->priv->path = g_strdup (g_value_get_string (value)); -        break; -    case PROP_FD: -        device->priv->fd = dup (g_value_get_int (value)); -        break; -    default: -        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); -        break; -    } -} +    g_clear_object (&device->priv->input); +    g_clear_object (&device->priv->output); -static void -oss_device_init (OssDevice *device) -{ -    device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, -                                                OSS_TYPE_DEVICE, -                                                OssDevicePrivate); +    G_OBJECT_CLASS (oss_device_parent_class)->dispose (object);  }  static void  oss_device_finalize (GObject *object)  { -    OssDevice *device; - -    device = OSS_DEVICE (object); - -    g_free (device->priv->name); -    g_free (device->priv->description); +    OssDevice *device = OSS_DEVICE (object); -    if (device->priv->fd != -1) -        g_close (device->priv->fd, NULL); +    close (device->priv->fd);      G_OBJECT_CLASS (oss_device_parent_class)->finalize (object);  }  OssDevice * -oss_device_new (const gchar *path, gint fd) +oss_device_new (const gchar *name, const gchar *label, const gchar *path, gint fd)  {      OssDevice *device; -    gchar     *basename; +    gchar     *stream_name; -    g_return_val_if_fail (path != NULL, NULL); +    g_return_val_if_fail (name  != NULL, NULL); +    g_return_val_if_fail (label != NULL, NULL); +    g_return_val_if_fail (path  != NULL, NULL);      device = g_object_new (OSS_TYPE_DEVICE, -                           "path", path, -                           "fd", fd, +                           "name", name, +                           "label", label, +                           "icon", OSS_DEVICE_ICON,                             NULL); -    basename = g_path_get_basename (path); +    device->priv->fd   = dup (fd); +    device->priv->path = g_strdup (path); -    device->priv->name = g_strdup_printf ("oss-%s", basename); -    g_free (basename); +    stream_name = g_strdup_printf ("oss-input-%s", name); +    device->priv->input = oss_stream_new (stream_name, +                                          MATE_MIXER_DEVICE (device), +                                          MATE_MIXER_STREAM_INPUT); +    g_free (stream_name); + +    stream_name = g_strdup_printf ("oss-output-%s", name); +    device->priv->output = oss_stream_new (stream_name, +                                           MATE_MIXER_DEVICE (device), +                                           MATE_MIXER_STREAM_OUTPUT); +    g_free (stream_name);      return device;  }  gboolean -oss_device_read (OssDevice *device) +oss_device_open (OssDevice *device)  { -    gchar                  *name; -    gchar                  *basename; -    MateMixerStreamControl *ctl; +    gint ret;      g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); -    // XXX avoid calling this function repeatedly - -    g_debug ("Querying device %s (%s)", +    g_debug ("Opening device %s (%s)",               device->priv->path, -             device->priv->description); - -    basename = g_path_get_basename (device->priv->path); - -    name = g_strdup_printf ("oss-input-%s", basename); -    device->priv->input = MATE_MIXER_STREAM (oss_stream_new (name, -                                                             device->priv->description, -                                                             MATE_MIXER_STREAM_INPUT)); -    g_free (name); - -    name = g_strdup_printf ("oss-output-%s", basename); -    device->priv->output = MATE_MIXER_STREAM (oss_stream_new (name, -                                                              device->priv->description, -                                                              MATE_MIXER_STREAM_OUTPUT | -                                                              MATE_MIXER_STREAM_PORTS_FIXED)); -    g_free (name); - -    if (read_mixer_devices (device) == FALSE) -        return FALSE; - -    // XXX prefer active ports as default if there is no default - -    ctl = mate_mixer_stream_get_default_control (device->priv->input); -    if (ctl == NULL) { -        const GList *list = mate_mixer_stream_list_controls (device->priv->input); - -        if (list != NULL) { -            ctl = MATE_MIXER_STREAM_CONTROL (list->data); -            oss_stream_set_default_control (OSS_STREAM (device->priv->input), -                                            OSS_STREAM_CONTROL (ctl)); -        } else -            g_clear_object (&device->priv->input); +             mate_mixer_device_get_label (MATE_MIXER_DEVICE (device))); + +    /* Read the essential information about the device, these values are not +     * expected to change and will not be queried */ +    ret = ioctl (device->priv->fd, +                 MIXER_READ (SOUND_MIXER_DEVMASK), +                 &device->priv->devmask); +    if (ret != 0) +        goto fail; + +    ret = ioctl (device->priv->fd, +                 MIXER_READ (SOUND_MIXER_STEREODEVS), +                 &device->priv->stereodevs); +    if (ret < 0) +        goto fail; + +    ret = ioctl (device->priv->fd, +                 MIXER_READ (SOUND_MIXER_RECMASK), +                 &device->priv->recmask); +    if (ret < 0) +        goto fail; + +    /* The recording source mask may change at any time, here we just read +     * the initial value */ +    ret = ioctl (device->priv->fd, +                 MIXER_READ (SOUND_MIXER_RECSRC), +                 &device->priv->recsrc); +    if (ret < 0) +        goto fail; + +    /* NOTE: Linux also supports SOUND_MIXER_OUTSRC and SOUND_MIXER_OUTMASK which +     * inform about/enable input->output, we could potentially create toggles +     * for these, but these constants are not defined on any BSD. */ + +    return TRUE; + +fail: +    g_warning ("Failed to read device %s: %s", +               device->priv->path, +               g_strerror (errno)); + +    return FALSE; +} + +gboolean +oss_device_load (OssDevice *device) +{ +    MateMixerStreamControl *control; + +    g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); + +    read_mixer_devices (device); + +    control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->input)); +    if (control == NULL) { +        // XXX pick something      } -    if (ctl != NULL) +    if (control != NULL)          g_debug ("Default input stream control is %s", -                 mate_mixer_stream_control_get_description (ctl)); - -    ctl = mate_mixer_stream_get_default_control (device->priv->output); -    if (ctl == NULL) { -        const GList *list = mate_mixer_stream_list_controls (device->priv->output); - -        if (list != NULL) { -            ctl = MATE_MIXER_STREAM_CONTROL (list->data); -            oss_stream_set_default_control (OSS_STREAM (device->priv->output), -                                            OSS_STREAM_CONTROL (ctl)); -        } else -            g_clear_object (&device->priv->output); +                 mate_mixer_stream_control_get_label (control)); + +    control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->output)); +    if (control == NULL) { +        // XXX pick something      } -    if (ctl != NULL) +    if (control != NULL)          g_debug ("Default output stream control is %s", -                 mate_mixer_stream_control_get_description (ctl)); +                 mate_mixer_stream_control_get_label (control));      return TRUE;  } +gint +oss_device_get_fd (OssDevice *device) +{ +    g_return_val_if_fail (OSS_IS_DEVICE (device), -1); + +    return device->priv->fd; +} +  const gchar * -oss_device_get_path (OssDevice *odevice) +oss_device_get_path (OssDevice *device)  { -    g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); +    g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); -    return odevice->priv->path; +    return device->priv->path;  } -MateMixerStream * -oss_device_get_input_stream (OssDevice *odevice) +OssStream * +oss_device_get_input_stream (OssDevice *device)  { -    g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); +    g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); -    return odevice->priv->input; +    return device->priv->input;  } -MateMixerStream * -oss_device_get_output_stream (OssDevice *odevice) +OssStream * +oss_device_get_output_stream (OssDevice *device)  { -    g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); +    g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); -    return odevice->priv->output; +    return device->priv->output;  } -gboolean -oss_device_set_description (OssDevice *odevice, const gchar *description) +static GList * +oss_device_list_streams (MateMixerDevice *mmd)  { -    g_return_val_if_fail (OSS_IS_DEVICE (odevice), FALSE); +    OssDevice *device; +    GList     *list = NULL; -    if (g_strcmp0 (odevice->priv->description, description) != 0) { -        g_free (odevice->priv->description); +    g_return_val_if_fail (OSS_IS_DEVICE (mmd), NULL); -        odevice->priv->description = g_strdup (description); +    device = OSS_DEVICE (mmd); -        g_object_notify (G_OBJECT (odevice), "description"); -        return TRUE; -    } -    return FALSE; +    if (device->priv->output != NULL) +        list = g_list_prepend (list, g_object_ref (device->priv->output)); +    if (device->priv->input != NULL) +        list = g_list_prepend (list, g_object_ref (device->priv->input)); + +    return list;  } +#define OSS_MASK_HAS_DEVICE(mask,i) ((gboolean) (((mask) & (1 << (i))) > 0)) +  static gboolean  read_mixer_devices (OssDevice *device)  { -    gint              devmask, -                      stereomask, -                      recmask; -    gint              ret; -    gint              i; -    OssStreamControl *ctl; - -    ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_DEVMASK), &devmask); -    if (ret < 0) -        goto fail; -    ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_STEREODEVS), &stereomask); -    if (ret < 0) -        goto fail; -    ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_RECMASK), &recmask); -    if (ret < 0) -        goto fail; +    gint i; -    for (i = 0; i < MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES); i++) { -        gboolean       stereo; -        gboolean       input = FALSE; -        MateMixerPort *port = NULL; +    for (i = 0; i < OSS_N_DEVICES; i++) { +        OssStreamControl *control; +        gboolean          input = FALSE;          /* Skip unavailable controls */ -        if ((devmask & (1 << i)) == 0) +        if (OSS_MASK_HAS_DEVICE (device->priv->devmask, i) == FALSE)              continue; -        if ((stereomask & (1 << i)) > 0) -            stereo = TRUE; -        else -            stereo = FALSE; - -        if (oss_devices[i].flags == MATE_MIXER_STREAM_NO_FLAGS) { -            if ((recmask & (1 << i)) > 0) -                input = TRUE; +        if (oss_devices[i].type == OSS_DEV_ANY) { +            input = OSS_MASK_HAS_DEVICE (device->priv->recmask, i);          } -        if (oss_devices[i].flags == MATE_MIXER_STREAM_INPUT) { -            if ((recmask & (1 << i)) == 0) { -                g_debug ("Skipping non-input device %s", oss_devices[i].name); -                continue; -            } +        else if (oss_devices[i].type == OSS_DEV_INPUT) {              input = TRUE;          } -        ctl = oss_stream_control_new (device->priv->fd, -                                      i, -                                      oss_devices[i].name, -                                      oss_devices[i].description, -                                      stereo); - -        if (oss_devices[i].role == MATE_MIXER_STREAM_CONTROL_ROLE_PORT) -            port = _mate_mixer_port_new (oss_devices[i].name, -                                         oss_devices[i].description, -                                         NULL, -                                         0, -                                         0); +        control = oss_stream_control_new (oss_devices[i].name, +                                          oss_devices[i].label, +                                          oss_devices[i].role, +                                          device->priv->fd, +                                          i, +                                          OSS_MASK_HAS_DEVICE (device->priv->stereodevs, i));          if (input == TRUE) { -            oss_stream_add_control (OSS_STREAM (device->priv->input), ctl); +            oss_stream_add_control (OSS_STREAM (device->priv->input), control);              if (i == SOUND_MIXER_RECLEV || i == SOUND_MIXER_IGAIN) { -                if (i == SOUND_MIXER_RECLEV) { -                    oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); -                } else { -                    MateMixerStreamControl *defctl; - -                    defctl = mate_mixer_stream_get_default_control (device->priv->input); -                    if (defctl == NULL) -                        oss_stream_set_default_control (OSS_STREAM (device->priv->input), ctl); -                } +                if (i == SOUND_MIXER_RECLEV) +                    set_stream_default_control (OSS_STREAM (device->priv->input), +                                                control, +                                                TRUE); +                else +                    set_stream_default_control (OSS_STREAM (device->priv->input), +                                                control, +                                                FALSE);              } - -            if (port != NULL) -                oss_stream_add_port (OSS_STREAM (device->priv->input), port);          } else { -            oss_stream_add_control (OSS_STREAM (device->priv->output), ctl); - -            if (i == SOUND_MIXER_VOLUME) { -                oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); -            } -            else if (i == SOUND_MIXER_PCM) { -                MateMixerStreamControl *defctl; - -                defctl = mate_mixer_stream_get_default_control (device->priv->output); -                if (defctl == NULL) -                    oss_stream_set_default_control (OSS_STREAM (device->priv->output), ctl); +            oss_stream_add_control (OSS_STREAM (device->priv->output), control); + +            if (i == SOUND_MIXER_VOLUME || i == SOUND_MIXER_PCM) { +                if (i == SOUND_MIXER_VOLUME) +                    set_stream_default_control (OSS_STREAM (device->priv->output), +                                                control, +                                                TRUE); +                else +                    set_stream_default_control (OSS_STREAM (device->priv->output), +                                                control, +                                                FALSE);              } - -            if (port != NULL) -                oss_stream_add_port (OSS_STREAM (device->priv->output), port);          } -        if (port != NULL) -            oss_stream_control_set_port (ctl, port); - -        oss_stream_control_set_role (ctl, oss_devices[i].role); -          g_debug ("Added control %s", -                 mate_mixer_stream_control_get_description (MATE_MIXER_STREAM_CONTROL (ctl))); +                 mate_mixer_stream_control_get_label (MATE_MIXER_STREAM_CONTROL (control))); -        oss_stream_control_update (ctl); +        oss_stream_control_update (control);      }      return TRUE; - -fail: -    g_warning ("Failed to read device %s: %s", -               device->priv->path, -               g_strerror (errno)); - -    return FALSE;  } -static const gchar * -oss_device_get_name (MateMixerDevice *device) -{ -    g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - -    return OSS_DEVICE (device)->priv->name; -} - -static const gchar * -oss_device_get_description (MateMixerDevice *device) -{ -    g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); - -    return OSS_DEVICE (device)->priv->description; -} - -static const gchar * -oss_device_get_icon (MateMixerDevice *device) +static gboolean +set_stream_default_control (OssStream *stream, OssStreamControl *control, gboolean force)  { -    g_return_val_if_fail (OSS_IS_DEVICE (device), NULL); +    MateMixerStreamControl *current; -    return OSS_DEVICE_ICON; +    current = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)); +    if (current == NULL || force == TRUE) { +        oss_stream_set_default_control (stream, OSS_STREAM_CONTROL (control)); +        return TRUE; +    } +    return FALSE;  } diff --git a/backends/oss/oss-device.h b/backends/oss/oss-device.h index fe40eb2..261a884 100644 --- a/backends/oss/oss-device.h +++ b/backends/oss/oss-device.h @@ -20,6 +20,9 @@  #include <glib.h>  #include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "oss-stream.h"  G_BEGIN_DECLS @@ -42,7 +45,7 @@ typedef struct _OssDevicePrivate  OssDevicePrivate;  struct _OssDevice  { -    GObject parent; +    MateMixerDevice parent;      /*< private >*/      OssDevicePrivate *priv; @@ -50,23 +53,24 @@ struct _OssDevice  struct _OssDeviceClass  { -    GObjectClass parent; +    MateMixerDeviceClass parent;  }; -GType            oss_device_get_type          (void) G_GNUC_CONST; - -OssDevice *      oss_device_new               (const gchar *path, -                                               gint         fd); +GType        oss_device_get_type          (void) G_GNUC_CONST; -gboolean         oss_device_read              (OssDevice   *device); +OssDevice *  oss_device_new               (const gchar *name, +                                           const gchar *label, +                                           const gchar *path, +                                           gint         fd); -const gchar *    oss_device_get_path          (OssDevice   *odevice); +gboolean     oss_device_open              (OssDevice   *device); +gboolean     oss_device_load              (OssDevice   *device); -MateMixerStream *oss_device_get_input_stream  (OssDevice   *odevice); -MateMixerStream *oss_device_get_output_stream (OssDevice   *odevice); +gint         oss_device_get_fd            (OssDevice   *device); +const gchar *oss_device_get_path          (OssDevice   *device); -gboolean         oss_device_set_description   (OssDevice   *odevice, -                                               const gchar *description); +OssStream *  oss_device_get_input_stream  (OssDevice   *device); +OssStream *  oss_device_get_output_stream (OssDevice   *device);  G_END_DECLS diff --git a/backends/oss/oss-stream-control.c b/backends/oss/oss-stream-control.c index 0b1db26..5161528 100644 --- a/backends/oss/oss-stream-control.c +++ b/backends/oss/oss-stream-control.c @@ -15,14 +15,13 @@   * License along with this library; if not, see <http://www.gnu.org/licenses/>.   */ -#include <unistd.h>  #include <errno.h>  #include <glib.h>  #include <glib/gstdio.h>  #include <glib-object.h> -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-stream-control.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h>  #include "oss-common.h"  #include "oss-stream-control.h" @@ -30,437 +29,279 @@  struct _OssStreamControlPrivate  {      gint                        fd; -    gint                        dev_number; -    gchar                      *name; -    gchar                      *description; +    gint                        devnum;      guint                       volume[2];      gfloat                      balance;      gboolean                    stereo; -    MateMixerPort              *port;      MateMixerStreamControlRole  role;      MateMixerStreamControlFlags flags;  }; -enum { -    PROP_0, -    PROP_NAME, -    PROP_DESCRIPTION, -    PROP_FLAGS, -    PROP_MUTE, -    PROP_VOLUME, -    PROP_BALANCE, -    PROP_FADE, -    PROP_FD, -    PROP_DEV_NUMBER, -    PROP_STEREO -}; - -static void mate_mixer_stream_control_interface_init (MateMixerStreamControlInterface *iface); - -static void oss_stream_control_class_init   (OssStreamControlClass *klass); +static void oss_stream_control_class_init (OssStreamControlClass *klass); -static void oss_stream_control_get_property (GObject               *object, -                                             guint                  param_id, -                                             GValue                *value, -                                             GParamSpec            *pspec); -static void oss_stream_control_set_property (GObject               *object, -                                             guint                  param_id, -                                             const GValue          *value, -                                             GParamSpec            *pspec); +static void oss_stream_control_init       (OssStreamControl      *control); +static void oss_stream_control_finalize   (GObject               *object); -static void oss_stream_control_init         (OssStreamControl      *octl); -static void oss_stream_control_finalize     (GObject               *object); +G_DEFINE_TYPE (OssStreamControl, oss_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL) -G_DEFINE_TYPE_WITH_CODE (OssStreamControl, oss_stream_control, G_TYPE_OBJECT, -                         G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM_CONTROL, -                                                mate_mixer_stream_control_interface_init)) +static gboolean                 oss_stream_control_set_mute             (MateMixerStreamControl  *mmsc, +                                                                         gboolean                 mute); -static const gchar *            oss_stream_control_get_name             (MateMixerStreamControl  *ctl); -static const gchar *            oss_stream_control_get_description      (MateMixerStreamControl  *ctl); +static guint                    oss_stream_control_get_num_channels     (MateMixerStreamControl  *mmsc); -static guint                    oss_stream_control_get_num_channels     (MateMixerStreamControl  *ctl); +static guint                    oss_stream_control_get_volume           (MateMixerStreamControl  *mmsc); -static gboolean                 oss_stream_control_set_volume           (MateMixerStreamControl  *ctl, +static gboolean                 oss_stream_control_set_volume           (MateMixerStreamControl  *mmsc,                                                                           guint                    volume); -static guint                    oss_stream_control_get_channel_volume   (MateMixerStreamControl  *ctl, +static gboolean                 oss_stream_control_has_channel_position (MateMixerStreamControl  *mmsc, +                                                                         MateMixerChannelPosition position); +static MateMixerChannelPosition oss_stream_control_get_channel_position (MateMixerStreamControl  *mmsc,                                                                           guint                    channel); -static gboolean                 oss_stream_control_set_channel_volume   (MateMixerStreamControl  *ctl, -                                                                         guint                    channel, -                                                                         guint                    volume); -static MateMixerChannelPosition oss_stream_control_get_channel_position (MateMixerStreamControl  *ctl, +static guint                    oss_stream_control_get_channel_volume   (MateMixerStreamControl  *mmsc,                                                                           guint                    channel); -static gboolean                 oss_stream_control_has_channel_position (MateMixerStreamControl  *ctl, -                                                                         MateMixerChannelPosition position); +static gboolean                 oss_stream_control_set_channel_volume   (MateMixerStreamControl  *mmsc, +                                                                         guint                    channel, +                                                                         guint                    volume); -static gboolean                 oss_stream_control_set_balance          (MateMixerStreamControl  *ctl, +static gboolean                 oss_stream_control_set_balance          (MateMixerStreamControl  *mmsc,                                                                           gfloat                   balance); -static guint                    oss_stream_control_get_min_volume       (MateMixerStreamControl  *ctl); -static guint                    oss_stream_control_get_max_volume       (MateMixerStreamControl  *ctl); -static guint                    oss_stream_control_get_normal_volume    (MateMixerStreamControl  *ctl); -static guint                    oss_stream_control_get_base_volume      (MateMixerStreamControl  *ctl); +static guint                    oss_stream_control_get_min_volume       (MateMixerStreamControl  *mmsc); +static guint                    oss_stream_control_get_max_volume       (MateMixerStreamControl  *mmsc); +static guint                    oss_stream_control_get_normal_volume    (MateMixerStreamControl  *mmsc); +static guint                    oss_stream_control_get_base_volume      (MateMixerStreamControl  *mmsc); -static void -mate_mixer_stream_control_interface_init (MateMixerStreamControlInterface *iface) -{ -    iface->get_name             = oss_stream_control_get_name; -    iface->get_description      = oss_stream_control_get_description; -    iface->get_num_channels     = oss_stream_control_get_num_channels; -    iface->set_volume           = oss_stream_control_set_volume; -    iface->get_channel_volume   = oss_stream_control_get_channel_volume; -    iface->set_channel_volume   = oss_stream_control_set_channel_volume; -    iface->get_channel_position = oss_stream_control_get_channel_position; -    iface->has_channel_position = oss_stream_control_has_channel_position; -    iface->set_balance          = oss_stream_control_set_balance; -    iface->get_min_volume       = oss_stream_control_get_min_volume; -    iface->get_max_volume       = oss_stream_control_get_max_volume; -    iface->get_normal_volume    = oss_stream_control_get_normal_volume; -    iface->get_base_volume      = oss_stream_control_get_base_volume; -} +static gboolean                 write_volume                            (OssStreamControl        *control, +                                                                         gint                     volume);  static void  oss_stream_control_class_init (OssStreamControlClass *klass)  { -    GObjectClass *object_class; +    GObjectClass                *object_class; +    MateMixerStreamControlClass *control_class;      object_class = G_OBJECT_CLASS (klass); -    object_class->finalize     = oss_stream_control_finalize; -    object_class->get_property = oss_stream_control_get_property; -    object_class->set_property = oss_stream_control_set_property; - -    g_object_class_install_property (object_class, -                                     PROP_FD, -                                     g_param_spec_int ("fd", -                                                       "File descriptor", -                                                       "File descriptor of the device", -                                                       G_MININT, -                                                       G_MAXINT, -                                                       -1, -                                                       G_PARAM_CONSTRUCT_ONLY | -                                                       G_PARAM_READWRITE | -                                                       G_PARAM_STATIC_STRINGS)); - -    g_object_class_install_property (object_class, -                                     PROP_DEV_NUMBER, -                                     g_param_spec_int ("dev-number", -                                                       "Dev number", -                                                       "OSS device number", -                                                       G_MININT, -                                                       G_MAXINT, -                                                       0, -                                                       G_PARAM_CONSTRUCT_ONLY | -                                                       G_PARAM_READWRITE | -                                                       G_PARAM_STATIC_STRINGS)); - -    g_object_class_install_property (object_class, -                                     PROP_STEREO, -                                     g_param_spec_boolean ("stereo", -                                                           "Stereo", -                                                           "Stereo", -                                                           FALSE, -                                                           G_PARAM_CONSTRUCT_ONLY | -                                                           G_PARAM_READWRITE | -                                                           G_PARAM_STATIC_STRINGS)); - -    g_object_class_override_property (object_class, PROP_NAME, "name"); -    g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); -    g_object_class_override_property (object_class, PROP_FLAGS, "flags"); -    g_object_class_override_property (object_class, PROP_MUTE, "mute"); -    g_object_class_override_property (object_class, PROP_VOLUME, "volume"); -    g_object_class_override_property (object_class, PROP_BALANCE, "balance"); -    g_object_class_override_property (object_class, PROP_FADE, "fade"); +    object_class->finalize = oss_stream_control_finalize; + +    control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); +    control_class->set_mute             = oss_stream_control_set_mute; +    control_class->get_num_channels     = oss_stream_control_get_num_channels; +    control_class->get_volume           = oss_stream_control_get_volume; +    control_class->set_volume           = oss_stream_control_set_volume; +    control_class->get_channel_volume   = oss_stream_control_get_channel_volume; +    control_class->set_channel_volume   = oss_stream_control_set_channel_volume; +    control_class->get_channel_position = oss_stream_control_get_channel_position; +    control_class->has_channel_position = oss_stream_control_has_channel_position; +    control_class->set_balance          = oss_stream_control_set_balance; +    control_class->get_min_volume       = oss_stream_control_get_min_volume; +    control_class->get_max_volume       = oss_stream_control_get_max_volume; +    control_class->get_normal_volume    = oss_stream_control_get_normal_volume; +    control_class->get_base_volume      = oss_stream_control_get_base_volume;      g_type_class_add_private (object_class, sizeof (OssStreamControlPrivate));  }  static void -oss_stream_control_get_property (GObject    *object, -                                 guint       param_id, -                                 GValue     *value, -                                 GParamSpec *pspec) -{ -    OssStreamControl *octl; - -    octl = OSS_STREAM_CONTROL (object); - -    switch (param_id) { -    case PROP_NAME: -        g_value_set_string (value, octl->priv->name); -        break; -    case PROP_DESCRIPTION: -        g_value_set_string (value, octl->priv->description); -        break; -    case PROP_FLAGS: -        g_value_set_flags (value, octl->priv->flags); -        break; -    case PROP_MUTE: -        /* Not supported */ -        g_value_set_boolean (value, FALSE); -        break; -    case PROP_VOLUME: -        g_value_set_uint (value, MAX (octl->priv->volume[0], octl->priv->volume[1])); -        break; -    case PROP_BALANCE: -        g_value_set_float (value, octl->priv->balance); -        break; -    case PROP_FADE: -        /* Not supported */ -        g_value_set_float (value, 0.0f); -        break; -    case PROP_FD: -        g_value_set_int (value, octl->priv->fd); -        break; -    case PROP_DEV_NUMBER: -        g_value_set_int (value, octl->priv->dev_number); -        break; -    case PROP_STEREO: -        g_value_set_boolean (value, octl->priv->stereo); -        break; -    default: -        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); -        break; -    } -} - -static void -oss_stream_control_set_property (GObject      *object, -                                 guint         param_id, -                                 const GValue *value, -                                 GParamSpec   *pspec) +oss_stream_control_init (OssStreamControl *control)  { -    OssStreamControl *octl; - -    octl = OSS_STREAM_CONTROL (object); - -    switch (param_id) { -    case PROP_FD: -        octl->priv->fd = dup (g_value_get_int (value)); -        break; -    case PROP_DEV_NUMBER: -        octl->priv->dev_number = g_value_get_int (value); -        break; -    case PROP_STEREO: -        octl->priv->stereo = g_value_get_boolean (value); -        break; -    default: -        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); -        break; -    } -} - -static void -oss_stream_control_init (OssStreamControl *octl) -{ -    octl->priv = G_TYPE_INSTANCE_GET_PRIVATE (octl, -                                              OSS_TYPE_STREAM_CONTROL, -                                              OssStreamControlPrivate); +    control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, +                                                 OSS_TYPE_STREAM_CONTROL, +                                                 OssStreamControlPrivate);  }  static void  oss_stream_control_finalize (GObject *object)  { -    OssStreamControl *octl; - -    octl = OSS_STREAM_CONTROL (object); +    OssStreamControl *control; -    g_free (octl->priv->name); -    g_free (octl->priv->description); +    control = OSS_STREAM_CONTROL (object); -    if (octl->priv->fd != -1) -        g_close (octl->priv->fd, NULL); +    close (control->priv->fd);      G_OBJECT_CLASS (oss_stream_control_parent_class)->finalize (object);  }  OssStreamControl * -oss_stream_control_new (gint         fd, -                        gint         dev_number, -                        const gchar *name, -                        const gchar *description, -                        gboolean     stereo) +oss_stream_control_new (const gchar               *name, +                        const gchar               *label, +                        MateMixerStreamControlRole role, +                        gint                       fd, +                        gint                       devnum, +                        gboolean                   stereo)  { -    OssStreamControl *ctl; - -    ctl = g_object_new (OSS_TYPE_STREAM_CONTROL, -                        "fd", fd, -                        "dev-number", dev_number, -                        "stereo", stereo, -                        NULL); - -    ctl->priv->name = g_strdup (name); -    ctl->priv->description = g_strdup (description); +    OssStreamControl           *control; +    MateMixerStreamControlFlags flags; -    return ctl; +    flags = MATE_MIXER_STREAM_CONTROL_HAS_VOLUME | +            MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME; +    if (stereo == TRUE) +        flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + +    control = g_object_new (OSS_TYPE_STREAM_CONTROL, +                            "name", name, +                            "label", label, +                            "flags", flags, +                            NULL); + +    control->priv->fd = fd; +    control->priv->devnum = devnum; +    control->priv->stereo = stereo; +    return control;  }  gboolean -oss_stream_control_update (OssStreamControl *octl) +oss_stream_control_update (OssStreamControl *control)  {      gint v;      gint ret; -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); -    ret = ioctl (octl->priv->fd, MIXER_READ (octl->priv->dev_number), &v); +    ret = ioctl (control->priv->fd, MIXER_READ (control->priv->devnum), &v);      if (ret < 0) {          g_warning ("Failed to read volume: %s", g_strerror (errno));          return FALSE;      } -    octl->priv->volume[0] = v & 0xFF; +    control->priv->volume[0] = v & 0xFF; -    if (octl->priv->stereo == TRUE) -        octl->priv->volume[1] = (v >> 8) & 0xFF; - -    return TRUE; -} - -gboolean -oss_stream_control_set_port (OssStreamControl *octl, MateMixerPort *port) -{ -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); +    if (control->priv->stereo == TRUE) { +        gfloat balance; +        guint  left; +        guint  right; -    // XXX provide property +        control->priv->volume[1] = (v >> 8) & 0xFF; -    if (octl->priv->port != NULL) -        g_object_unref (octl->priv->port); +        /* Calculate balance */ +        left  = control->priv->volume[0]; +        right = control->priv->volume[1]; +        if (left == right) +            balance = 0.0f; +        else if (left > right) +            balance = -1.0f + ((gfloat) right / (gfloat) left); +        else +            balance = +1.0f - ((gfloat) left / (gfloat) right); -    octl->priv->port = g_object_ref (port); +        _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), +                                                balance); +    }      return TRUE;  } -gboolean -oss_stream_control_set_role (OssStreamControl *octl, MateMixerStreamControlRole role) +static gboolean +oss_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute)  { -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); -    octl->priv->role = role; +    // TODO      return TRUE;  } -static const gchar * -oss_stream_control_get_name (MateMixerStreamControl *ctl) +static guint +oss_stream_control_get_num_channels (MateMixerStreamControl *mmsc)  { -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), NULL); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), 0); -    return OSS_STREAM_CONTROL (ctl)->priv->name; +    return (OSS_STREAM_CONTROL (mmsc)->priv->stereo == TRUE) ? 2 : 1;  } -static const gchar * -oss_stream_control_get_description (MateMixerStreamControl *ctl) +static guint +oss_stream_control_get_volume (MateMixerStreamControl *mmsc)  { -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), NULL); +    OssStreamControl *control; -    return OSS_STREAM_CONTROL (ctl)->priv->description; -} +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), 0); -static guint -oss_stream_control_get_num_channels (MateMixerStreamControl *ctl) -{ -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), 0); +    control = OSS_STREAM_CONTROL (mmsc); -    if (OSS_STREAM_CONTROL (ctl)->priv->stereo == TRUE) -        return 2; +    if (control->priv->stereo == TRUE) +        return MAX (control->priv->volume[0], control->priv->volume[1]);      else -        return 1; +        return control->priv->volume[0];  }  static gboolean -oss_stream_control_set_volume (MateMixerStreamControl *ctl, guint volume) +oss_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume)  { -    OssStreamControl *octl; -    int v; -    int ret; - -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); +    OssStreamControl *control; +    gint              v; -    octl = OSS_STREAM_CONTROL (ctl); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); -    /* Some backends may allow setting higher value than maximum, but not here */ -    if (volume > 100) -        volume = 100; +    control = OSS_STREAM_CONTROL (mmsc); -    v = volume; -    if (octl->priv->stereo == TRUE) +    v = CLAMP (volume, 0, 100); +    if (control->priv->stereo == TRUE)          v |= (volume & 0xFF) << 8; -    ret = ioctl (octl->priv->fd, MIXER_WRITE (octl->priv->dev_number), &v); -    if (ret < 0) { -        g_warning ("Failed to set volume: %s", g_strerror (errno)); -        return FALSE; -    } -    return TRUE; +    return write_volume (control, v);  }  static guint -oss_stream_control_get_channel_volume (MateMixerStreamControl *ctl, guint channel) +oss_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel)  { -    OssStreamControl *octl; +    OssStreamControl *control; -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), 0); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), 0); -    octl = OSS_STREAM_CONTROL (ctl); +    control = OSS_STREAM_CONTROL (mmsc); -    if (channel > 1) -        return 0; - -    /* Right channel on mono stream will always have zero volume */ -    return octl->priv->volume[channel]; +    if (control->priv->stereo == TRUE) { +        if (channel == 0 || channel == 1) +            return control->priv->volume[channel]; +    } else { +        if (channel == 0) +            return control->priv->volume[0]; +    } +    return 0;  }  static gboolean -oss_stream_control_set_channel_volume (MateMixerStreamControl *ctl, +oss_stream_control_set_channel_volume (MateMixerStreamControl *mmsc,                                         guint                   channel,                                         guint                   volume)  { -    OssStreamControl *octl; -    int ret; -    int v; +    OssStreamControl *control; +    gint              v; -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); -    if (channel > 1) -        return FALSE; - -    octl = OSS_STREAM_CONTROL (ctl); - -    /* Some backends may allow setting higher value than maximum, but not here */ -    if (volume > 100) -        volume = 100; - -    if (channel == 0) -        v = volume; -    else -        v = octl->priv->volume[0]; +    control = OSS_STREAM_CONTROL (mmsc); +    volume  = CLAMP (volume, 0, 100); -    if (channel == 1) { -        if (octl->priv->stereo == FALSE) +    if (control->priv->stereo == TRUE) { +        if (channel > 1)              return FALSE; -        v |= volume << 8; -    } else -        v |= octl->priv->volume[1] << 8; +        /* Stereo channel volume - left channel is in the lowest 8 bits and +         * right channel is in the higher 8 bits */ +        if (channel == 0) +            v = volume | (control->priv->volume[1] << 8); +        else +            v = control->priv->volume[0] | (volume << 8); +    } else { +        if (channel > 0) +            return FALSE; -    ret = ioctl (octl->priv->fd, MIXER_WRITE (octl->priv->dev_number), &v); -    if (ret < 0) { -        g_warning ("Failed to set channel volume: %s", g_strerror (errno)); -        return FALSE; +        /* Single channel volume - only lowest 8 bits are used */ +        v = volume;      } -    return TRUE; + +    return write_volume (control, v);  }  static MateMixerChannelPosition -oss_stream_control_get_channel_position (MateMixerStreamControl *ctl, guint channel) +oss_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel)  { -    OssStreamControl *octl; +    OssStreamControl *control; -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), MATE_MIXER_CHANNEL_UNKNOWN); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN); -    octl = OSS_STREAM_CONTROL (ctl); +    control = OSS_STREAM_CONTROL (mmsc); -    if (octl->priv->stereo == TRUE) { +    if (control->priv->stereo == TRUE) {          if (channel == 0)              return MATE_MIXER_CHANNEL_FRONT_LEFT;          else if (channel == 1) @@ -473,57 +314,79 @@ oss_stream_control_get_channel_position (MateMixerStreamControl *ctl, guint chan  }  static gboolean -oss_stream_control_has_channel_position (MateMixerStreamControl  *ctl, +oss_stream_control_has_channel_position (MateMixerStreamControl  *mmsc,                                           MateMixerChannelPosition position)  { -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE);      if (position == MATE_MIXER_CHANNEL_MONO) -        return OSS_STREAM_CONTROL (ctl)->priv->stereo == FALSE; +        return OSS_STREAM_CONTROL (mmsc)->priv->stereo == FALSE;      if (position == MATE_MIXER_CHANNEL_FRONT_LEFT ||          position == MATE_MIXER_CHANNEL_FRONT_RIGHT) -        return OSS_STREAM_CONTROL (ctl)->priv->stereo == TRUE; +        return OSS_STREAM_CONTROL (mmsc)->priv->stereo == TRUE;      return FALSE;  }  static gboolean -oss_stream_control_set_balance (MateMixerStreamControl *ctl, gfloat balance) +oss_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance)  { -    OssStreamControl *octl; +    OssStreamControl *control; +    guint             max; +    gint              v; -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (ctl), FALSE); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); -    octl = OSS_STREAM_CONTROL (ctl); +    control = OSS_STREAM_CONTROL (mmsc); -    if (octl->priv->stereo == FALSE) -        return FALSE; +    max = MAX (control->priv->volume[0], control->priv->volume[1]); +    if (balance <= 0) { +        control->priv->volume[1] = (balance + 1.0f) * max; +        control->priv->volume[0] = max; +    } else { +        control->priv->volume[0] = (1.0f - balance) * max; +        control->priv->volume[1] = max; +    } -    // XXX -    return TRUE; +    v = control->priv->volume[0] | (control->priv->volume[1] << 8); + +    return write_volume (control, v);  }  static guint -oss_stream_control_get_min_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_min_volume (MateMixerStreamControl *mmsc)  {      return 0;  }  static guint -oss_stream_control_get_max_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_max_volume (MateMixerStreamControl *mmsc)  {      return 100;  }  static guint -oss_stream_control_get_normal_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_normal_volume (MateMixerStreamControl *mmsc)  {      return 100;  }  static guint -oss_stream_control_get_base_volume (MateMixerStreamControl *ctl) +oss_stream_control_get_base_volume (MateMixerStreamControl *mmsc)  {      return 100;  } + +static gboolean +write_volume (OssStreamControl *control, gint volume) +{ +    gint ret; + +    ret = ioctl (control->priv->fd, MIXER_WRITE (control->priv->devnum), &volume); +    if (ret < 0) { +        g_warning ("Failed to set volume: %s", g_strerror (errno)); +        return FALSE; +    } +    return TRUE; +} diff --git a/backends/oss/oss-stream-control.h b/backends/oss/oss-stream-control.h index 420af48..c839faf 100644 --- a/backends/oss/oss-stream-control.h +++ b/backends/oss/oss-stream-control.h @@ -20,6 +20,7 @@  #include <glib.h>  #include <glib-object.h> +#include <libmatemixer/matemixer.h>  G_BEGIN_DECLS @@ -42,7 +43,7 @@ typedef struct _OssStreamControlPrivate  OssStreamControlPrivate;  struct _OssStreamControl  { -    GObject parent; +    MateMixerStreamControl parent;      /*< private >*/      OssStreamControlPrivate *priv; @@ -50,24 +51,19 @@ struct _OssStreamControl  struct _OssStreamControlClass  { -    GObjectClass parent; +    MateMixerStreamControlClass parent;  }; -GType             oss_stream_control_get_type    (void) G_GNUC_CONST; +GType             oss_stream_control_get_type (void) G_GNUC_CONST; -OssStreamControl *oss_stream_control_new         (gint         fd, -                                                  gint         dev_number, -                                                  const gchar *name, -                                                  const gchar *description, -                                                  gboolean     stereo); +OssStreamControl *oss_stream_control_new      (const gchar               *name, +                                               const gchar               *label, +                                               MateMixerStreamControlRole role, +                                               gint                       fd, +                                               gint                       devnum, +                                               gboolean                   stereo); -gboolean          oss_stream_control_update      (OssStreamControl *octl); - -gboolean          oss_stream_control_set_port    (OssStreamControl *octl, -                                                  MateMixerPort    *port); - -gboolean          oss_stream_control_set_role    (OssStreamControl *octl, -                                                  MateMixerStreamControlRole role); +gboolean          oss_stream_control_update   (OssStreamControl          *control);  G_END_DECLS diff --git a/backends/oss/oss-stream.c b/backends/oss/oss-stream.c index 69bfd06..5f0c629 100644 --- a/backends/oss/oss-stream.c +++ b/backends/oss/oss-stream.c @@ -15,301 +15,169 @@   * License along with this library; if not, see <http://www.gnu.org/licenses/>.   */ -#include <string.h>  #include <glib.h>  #include <glib-object.h> +#include <libmatemixer/matemixer.h> -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> - +#include "oss-device.h"  #include "oss-stream.h"  #include "oss-stream-control.h"  struct _OssStreamPrivate  { -    gchar                 *name; -    gchar                 *description; -    MateMixerDevice       *device; -    MateMixerStreamFlags   flags; -    MateMixerStreamState   state; -    GHashTable            *ports; -    GList                 *ports_list; -    GHashTable            *controls; -    GList                 *controls_list; -    OssStreamControl      *control; -}; - -enum { -    PROP_0, -    PROP_NAME, -    PROP_DESCRIPTION, -    PROP_DEVICE, -    PROP_FLAGS, -    PROP_STATE, -    PROP_ACTIVE_PORT, +    GHashTable       *controls; +    OssStreamControl *control;  }; -static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); -  static void oss_stream_class_init   (OssStreamClass *klass); -static void oss_stream_get_property (GObject        *object, -                                     guint           param_id, -                                     GValue         *value, -                                     GParamSpec     *pspec); -  static void oss_stream_init         (OssStream      *ostream);  static void oss_stream_dispose      (GObject        *object);  static void oss_stream_finalize     (GObject        *object); -G_DEFINE_TYPE_WITH_CODE (OssStream, oss_stream, G_TYPE_OBJECT, -                         G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM, -                                                mate_mixer_stream_interface_init)) - -static const gchar *           oss_stream_get_name            (MateMixerStream *stream); -static const gchar *           oss_stream_get_description     (MateMixerStream *stream); +G_DEFINE_TYPE (OssStream, oss_stream, MATE_MIXER_TYPE_STREAM)  static MateMixerStreamControl *oss_stream_get_control         (MateMixerStream *stream,                                                                 const gchar     *name);  static MateMixerStreamControl *oss_stream_get_default_control (MateMixerStream *stream); -static const GList *           oss_stream_list_controls       (MateMixerStream *stream); -static const GList *           oss_stream_list_ports          (MateMixerStream *stream); - -static void -mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) -{ -    iface->get_name            = oss_stream_get_name; -    iface->get_description     = oss_stream_get_description; -    iface->get_control         = oss_stream_get_control; -    iface->get_default_control = oss_stream_get_default_control; -    iface->list_controls       = oss_stream_list_controls; -    iface->list_ports          = oss_stream_list_ports; -} +static GList *                 oss_stream_list_controls       (MateMixerStream *stream);  static void  oss_stream_class_init (OssStreamClass *klass)  { -    GObjectClass *object_class; +    GObjectClass         *object_class; +    MateMixerStreamClass *stream_class;      object_class = G_OBJECT_CLASS (klass); -    object_class->dispose      = oss_stream_dispose; -    object_class->finalize     = oss_stream_finalize; -    object_class->get_property = oss_stream_get_property; +    object_class->dispose  = oss_stream_dispose; +    object_class->finalize = oss_stream_finalize; -    g_object_class_override_property (object_class, PROP_NAME, "name"); -    g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); -    g_object_class_override_property (object_class, PROP_DEVICE, "device"); -    g_object_class_override_property (object_class, PROP_FLAGS, "flags"); -    g_object_class_override_property (object_class, PROP_STATE, "state"); -    g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port"); +    stream_class = MATE_MIXER_STREAM_CLASS (klass); +    stream_class->get_control         = oss_stream_get_control; +    stream_class->get_default_control = oss_stream_get_default_control; +    stream_class->list_controls       = oss_stream_list_controls;      g_type_class_add_private (object_class, sizeof (OssStreamPrivate));  }  static void -oss_stream_get_property (GObject    *object, -                         guint       param_id, -                         GValue     *value, -                         GParamSpec *pspec) +oss_stream_init (OssStream *stream)  { -    OssStream *ostream; - -    ostream = OSS_STREAM (object); - -    switch (param_id) { -    case PROP_NAME: -        g_value_set_string (value, ostream->priv->name); -        break; -    case PROP_DESCRIPTION: -        g_value_set_string (value, ostream->priv->description); -        break; -    case PROP_DEVICE: -        g_value_set_object (value, ostream->priv->device); -        break; -    case PROP_FLAGS: -        g_value_set_flags (value, ostream->priv->flags); -        break; -    case PROP_STATE: -        /* Not supported */ -        g_value_set_enum (value, MATE_MIXER_STREAM_STATE_UNKNOWN); -        break; -    case PROP_ACTIVE_PORT: -        // XXX -        g_value_set_object (value, NULL); -        break; -    default: -        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); -        break; -    } -} +    stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, +                                                OSS_TYPE_STREAM, +                                                OssStreamPrivate); -static void -oss_stream_init (OssStream *ostream) -{ -    ostream->priv = G_TYPE_INSTANCE_GET_PRIVATE (ostream, -                                                 OSS_TYPE_STREAM, -                                                 OssStreamPrivate); - -    ostream->priv->controls = g_hash_table_new_full (g_str_hash, -                                                     g_str_equal, -                                                     g_free, -                                                     g_object_unref); - -    ostream->priv->ports = g_hash_table_new_full (g_str_hash, -                                                  g_str_equal, -                                                  g_free, -                                                  g_object_unref); +    stream->priv->controls = g_hash_table_new_full (g_str_hash, +                                                    g_str_equal, +                                                    g_free, +                                                    g_object_unref);  }  static void  oss_stream_dispose (GObject *object)  { -    OssStream *ostream; +    OssStream *stream; -    ostream = OSS_STREAM (object); +    stream = OSS_STREAM (object); -    g_hash_table_remove_all (ostream->priv->controls); -    g_hash_table_remove_all (ostream->priv->ports); +    g_clear_object (&stream->priv->control); +    g_hash_table_remove_all (stream->priv->controls); -    G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object); +    G_OBJECT_CLASS (oss_stream_parent_class)->dispose (object);  }  static void  oss_stream_finalize (GObject *object)  { -    OssStream *ostream; - -    ostream = OSS_STREAM (object); +    OssStream *stream; -    g_free (ostream->priv->name); -    g_free (ostream->priv->description); +    stream = OSS_STREAM (object); -    g_hash_table_destroy (ostream->priv->controls); -    g_hash_table_destroy (ostream->priv->ports); +    g_hash_table_destroy (stream->priv->controls);      G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object);  }  OssStream *  oss_stream_new (const gchar         *name, -                const gchar         *description, +                MateMixerDevice     *device,                  MateMixerStreamFlags flags)  {      OssStream *stream; -    stream = g_object_new (OSS_TYPE_STREAM, NULL); - -    stream->priv->name = g_strdup (name); -    stream->priv->description = g_strdup (description); -    stream->priv->flags = flags; - +    stream = g_object_new (OSS_TYPE_STREAM, +                           "name", name, +                           "device", device, +                           "flags", flags, +                           NULL);      return stream;  }  gboolean -oss_stream_add_control (OssStream *ostream, OssStreamControl *octl) +oss_stream_add_control (OssStream *stream, OssStreamControl *control)  {      const gchar *name; -    g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); +    g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); -    name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (octl)); +    name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control)); -    g_hash_table_insert (ostream->priv->controls, +    g_hash_table_insert (stream->priv->controls,                           g_strdup (name), -                         octl); +                         control);      return TRUE;  }  gboolean -oss_stream_set_default_control (OssStream *ostream, OssStreamControl *octl) +oss_stream_set_default_control (OssStream *stream, OssStreamControl *control)  { -    g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); -    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (octl), FALSE); +    g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE); +    g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE);      /* This function is only used internally so avoid validating that the control       * belongs to this stream */ -    if (ostream->priv->control != NULL) -        g_object_unref (ostream->priv->control); - -    ostream->priv->control = g_object_ref (octl); -    return TRUE; -} +    if (stream->priv->control != NULL) +        g_object_unref (stream->priv->control); -gboolean -oss_stream_add_port (OssStream *ostream, MateMixerPort *port) -{ -    g_return_val_if_fail (OSS_IS_STREAM (ostream), FALSE); -    g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); +    if G_LIKELY (control != NULL) +        stream->priv->control = g_object_ref (control); +    else +        stream->priv->control = NULL; -    g_hash_table_insert (ostream->priv->ports, -                         g_strdup (mate_mixer_port_get_name (port)), -                         port);      return TRUE;  } -static const gchar * -oss_stream_get_name (MateMixerStream *stream) -{ -    g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - -    return OSS_STREAM (stream)->priv->name; -} - -static const gchar * -oss_stream_get_description (MateMixerStream *stream) -{ -    g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - -    return OSS_STREAM (stream)->priv->description; -} -  static MateMixerStreamControl * -oss_stream_get_control (MateMixerStream *stream, const gchar *name) +oss_stream_get_control (MateMixerStream *mms, const gchar *name)  { -    g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); +    g_return_val_if_fail (OSS_IS_STREAM (mms), NULL);      g_return_val_if_fail (name != NULL, NULL); -    return g_hash_table_lookup (OSS_STREAM (stream)->priv->controls, name); +    return g_hash_table_lookup (OSS_STREAM (mms)->priv->controls, name);  }  static MateMixerStreamControl * -oss_stream_get_default_control (MateMixerStream *stream) -{ -    g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - -    return MATE_MIXER_STREAM_CONTROL (OSS_STREAM (stream)->priv->control); -} - -static const GList * -oss_stream_list_controls (MateMixerStream *stream) +oss_stream_get_default_control (MateMixerStream *mms)  { -    OssStream *ostream; - -    g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); - -    ostream = OSS_STREAM (stream); +    g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); -    if (ostream->priv->controls_list == NULL) -        ostream->priv->controls_list = g_hash_table_get_values (ostream->priv->controls); - -    return ostream->priv->controls_list; +    return MATE_MIXER_STREAM_CONTROL (OSS_STREAM (mms)->priv->control);  } -static const GList * -oss_stream_list_ports (MateMixerStream *stream) +static GList * +oss_stream_list_controls (MateMixerStream *mms)  { -    OssStream *ostream; - -    g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); +    GList *list; -    ostream = OSS_STREAM (stream); +    g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); -    if (ostream->priv->ports_list == NULL) -        ostream->priv->ports_list = g_hash_table_get_values (ostream->priv->ports); +    /* Convert the hash table to a linked list, this list is expected to be +     * cached in the main library */ +    list = g_hash_table_get_values (OSS_STREAM (mms)->priv->controls); +    if (list != NULL) +        g_list_foreach (list, (GFunc) g_object_ref, NULL); -    return ostream->priv->ports_list; +    return list;  } diff --git a/backends/oss/oss-stream.h b/backends/oss/oss-stream.h index d6c2fb2..0470eb7 100644 --- a/backends/oss/oss-stream.h +++ b/backends/oss/oss-stream.h @@ -20,7 +20,9 @@  #include <glib.h>  #include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include "oss-device.h"  #include "oss-stream-control.h"  G_BEGIN_DECLS @@ -44,7 +46,7 @@ typedef struct _OssStreamPrivate  OssStreamPrivate;  struct _OssStream  { -    GObject parent; +    MateMixerStream parent;      /*< private >*/      OssStreamPrivate *priv; @@ -52,23 +54,20 @@ struct _OssStream  struct _OssStreamClass  { -    GObjectClass parent; +    MateMixerStreamClass parent;  }; -GType             oss_stream_get_type            (void) G_GNUC_CONST; +GType      oss_stream_get_type            (void) G_GNUC_CONST; -OssStream *       oss_stream_new                 (const gchar         *name, -                                                  const gchar         *description, -                                                  MateMixerStreamFlags flags); +OssStream *oss_stream_new                 (const gchar         *name, +                                           MateMixerDevice     *device, +                                           MateMixerStreamFlags flags); -gboolean          oss_stream_add_control         (OssStream           *stream, -                                                  OssStreamControl    *ctl); +gboolean   oss_stream_add_control         (OssStream           *stream, +                                           OssStreamControl    *control); -gboolean          oss_stream_set_default_control (OssStream           *stream, -                                                  OssStreamControl    *ctl); - -gboolean          oss_stream_add_port            (OssStream           *ostream, -                                                  MateMixerPort       *port); +gboolean   oss_stream_set_default_control (OssStream           *stream, +                                           OssStreamControl    *control);  G_END_DECLS diff --git a/backends/oss4/Makefile.am b/backends/oss4/Makefile.am deleted file mode 100644 index cca8723..0000000 --- a/backends/oss4/Makefile.am +++ /dev/null @@ -1,29 +0,0 @@ -backenddir = $(libdir)/libmatemixer - -backend_LTLIBRARIES = libmatemixer-oss4.la - -AM_CPPFLAGS =                                                   \ -        -I$(top_srcdir)                                         \ -        -DG_LOG_DOMAIN=\"libmatemixer-oss4\" - -libmatemixer_oss4_la_CFLAGS =                                   \ -        $(GLIB_CFLAGS)                                          \ -        $(OSS4_CFLAGS) - -libmatemixer_oss4_la_SOURCES =                                  \ -        oss4-common.h                                           \ -        oss4-backend.c                                          \ -        oss4-backend.h                                          \ -        oss4-device.c                                           \ -        oss4-device.h - -libmatemixer_oss4_la_LIBADD =                                   \ -        $(GLIB_LIBS) - -libmatemixer_oss4_la_LDFLAGS =                                  \ -        -avoid-version                                          \ -        -no-undefined                                           \ -        -export-dynamic                                         \ -        -module - --include $(top_srcdir)/git.mk diff --git a/backends/oss4/oss4-backend.c b/backends/oss4/oss4-backend.c deleted file mode 100644 index bd79fdf..0000000 --- a/backends/oss4/oss4-backend.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <glib.h> -#include <glib-object.h> -#include <glib/gstdio.h> -#include <glib/gprintf.h> -#include <glib/gi18n.h> -#include <gio/gio.h> - -#include <libmatemixer/matemixer-backend.h> -#include <libmatemixer/matemixer-backend-module.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> - -#include "oss4-backend.h" -#include "oss4-common.h" -#include "oss4-device.h" - -#define BACKEND_NAME      "OSS4" -#define BACKEND_PRIORITY  8 - -#define PATH_SNDSTAT      "/dev/sndstat" - -struct _Oss4BackendPrivate -{ -    gint fd; -    gchar           *sndstat; -    GHashTable      *devices; -    GHashTable      *streams; -    MateMixerStream *default_input; -    MateMixerStream *default_output; -    MateMixerState   state; -}; - -enum { -    PROP_0, -    PROP_STATE, -    PROP_DEFAULT_INPUT, -    PROP_DEFAULT_OUTPUT -}; - -static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); - -static void oss4_backend_class_init     (Oss4BackendClass *klass); -static void oss4_backend_class_finalize (Oss4BackendClass *klass); - -static void oss4_backend_get_property   (GObject         *object, -                                         guint            param_id, -                                         GValue          *value, -                                         GParamSpec      *pspec); - -static void oss4_backend_init           (Oss4Backend     *oss); -static void oss4_backend_dispose        (GObject         *object); -static void oss4_backend_finalize       (GObject         *object); - -G_DEFINE_DYNAMIC_TYPE_EXTENDED (Oss4Backend, oss4_backend, -                                G_TYPE_OBJECT, 0, -                                G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, -                                                               mate_mixer_backend_interface_init)) - -static gboolean oss4_backend_open         (MateMixerBackend  *backend); -static void     oss4_backend_close        (MateMixerBackend  *backend); -static GList *  oss4_backend_list_devices (MateMixerBackend  *backend); -static GList *  oss4_backend_list_streams (MateMixerBackend  *backend); - -static void     change_state                    (Oss4Backend       *oss, -                                                 MateMixerState     state); - -static gboolean read_device (Oss4Backend *oss, gint index); - -static gchar *  read_device_sndstat_description (Oss4Backend *oss, -                                                 const gchar *prefix); - -static void     add_device      (Oss4Backend *oss, Oss4Device *device); -static void     remove_device   (Oss4Backend *oss, Oss4Device *device); - -static MateMixerBackendInfo info; - -void -backend_module_init (GTypeModule *module) -{ -    oss4_backend_register_type (module); - -    info.name         = BACKEND_NAME; -    info.priority     = BACKEND_PRIORITY; -    info.g_type       = OSS4_TYPE_BACKEND; -    info.backend_type = MATE_MIXER_BACKEND_OSS4; -} - -const MateMixerBackendInfo *backend_module_get_info (void) -{ -    return &info; -} - -static void -mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) -{ -    iface->open         = oss4_backend_open; -    iface->close        = oss4_backend_close; -    iface->list_devices = oss4_backend_list_devices; -    iface->list_streams = oss4_backend_list_streams; -} - -static void -oss4_backend_class_init (Oss4BackendClass *klass) -{ -    GObjectClass *object_class; - -    object_class = G_OBJECT_CLASS (klass); -    object_class->dispose      = oss4_backend_dispose; -    object_class->finalize     = oss4_backend_finalize; -    object_class->get_property = oss4_backend_get_property; - -    g_object_class_override_property (object_class, PROP_STATE, "state"); -    g_object_class_override_property (object_class, PROP_DEFAULT_INPUT, "default-input"); -    g_object_class_override_property (object_class, PROP_DEFAULT_OUTPUT, "default-output"); - -    g_type_class_add_private (object_class, sizeof (Oss4BackendPrivate)); -} - -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ -static void -oss4_backend_class_finalize (Oss4BackendClass *klass) -{ -} - -static void -oss4_backend_get_property (GObject    *object, -                           guint       param_id, -                           GValue     *value, -                           GParamSpec *pspec) -{ -    Oss4Backend *oss; - -    oss = OSS4_BACKEND (object); - -    switch (param_id) { -    case PROP_STATE: -        g_value_set_enum (value, oss->priv->state); -        break; -    case PROP_DEFAULT_INPUT: -        g_value_set_object (value, oss->priv->default_input); -        break; -    case PROP_DEFAULT_OUTPUT: -        g_value_set_object (value, oss->priv->default_output); -        break; -        break; -    default: -        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); -        break; -    } -} - -static void -oss4_backend_init (Oss4Backend *oss) -{ -    oss->priv = G_TYPE_INSTANCE_GET_PRIVATE (oss, -                                             OSS4_TYPE_BACKEND, -                                             Oss4BackendPrivate); - -    oss->priv->devices = g_hash_table_new_full (g_direct_hash, -                                                g_direct_equal, -                                                NULL, -                                                g_object_unref); - -    oss->priv->streams = g_hash_table_new_full (g_str_hash, -                                                g_str_equal, -                                                g_free, -                                                g_object_unref); -} - -static void -oss4_backend_dispose (GObject *object) -{ -    oss4_backend_close (MATE_MIXER_BACKEND (object)); -} - -static void -oss4_backend_finalize (GObject *object) -{ -    Oss4Backend *oss; - -    oss = OSS4_BACKEND (object); - -    g_hash_table_destroy (oss->priv->devices); -    g_hash_table_destroy (oss->priv->streams); -} - -static gboolean -oss4_backend_open (MateMixerBackend *backend) -{ -    Oss4Backend       *oss; -    gint               fd; -    gint               i; -    gint               ret; -    struct oss_sysinfo info; - -    g_return_val_if_fail (OSS4_IS_BACKEND (backend), FALSE); - -    oss = OSS4_BACKEND (backend); - -    fd = g_open ("/dev/mixer", O_RDONLY, 0); -    if (fd == -1) -        fd = g_open ("/dev/mixer0", O_RDONLY, 0); -    if (fd == -1) { -        change_state (oss, MATE_MIXER_STATE_FAILED); -        return FALSE; -    } - -    /* Query the number of mixer devices */ -    ret = ioctl (fd, OSS_SYSINFO, &info); -    if (ret == -1) { -        close (fd); -        change_state (oss, MATE_MIXER_STATE_FAILED); -        return FALSE; -    } - -    g_debug ("The sound system is %s version %s", -             info.product, -             info.version); - -#if !defined(__linux__) -    /* At least on systems based on FreeBSD we will need to read devices names -     * from the sndstat file, but avoid even trying that on systems where this -     * is not needed and the file is not present */ -    oss->priv->sndstat = PATH_SNDSTAT; -#endif - -    oss->priv->fd = fd; - -    for (i = 0; i < info.nummixers; i++) -        read_device (oss, i); - -    change_state (oss, MATE_MIXER_STATE_READY); -    return TRUE; -} - -void -oss4_backend_close (MateMixerBackend *backend) -{ -    Oss4Backend *oss; - -    g_return_if_fail (OSS4_IS_BACKEND (backend)); - -    oss = OSS4_BACKEND (backend); - -    g_clear_object (&oss->priv->default_input); -    g_clear_object (&oss->priv->default_output); - -    g_hash_table_remove_all (oss->priv->streams); -    g_hash_table_remove_all (oss->priv->devices); -} - -static GList * -oss4_backend_list_devices (MateMixerBackend *backend) -{ -    GList *list; - -    g_return_val_if_fail (OSS4_IS_BACKEND (backend), NULL); - -    /* Convert the hash table to a sorted linked list, this list is expected -     * to be cached in the main library */ -    list = g_hash_table_get_values (OSS4_BACKEND (backend)->priv->devices); -    if (list != NULL) { -        g_list_foreach (list, (GFunc) g_object_ref, NULL); - -        return list; -    } -    return NULL; -} - -static GList * -oss4_backend_list_streams (MateMixerBackend *backend) -{ -    GList *list; - -    g_return_val_if_fail (OSS4_IS_BACKEND (backend), NULL); - -    /* Convert the hash table to a sorted linked list, this list is expected -     * to be cached in the main library */ -    list = g_hash_table_get_values (OSS4_BACKEND (backend)->priv->streams); -    if (list != NULL) { -        g_list_foreach (list, (GFunc) g_object_ref, NULL); - -        return list; -    } -    return NULL; -} - -static void -change_state (Oss4Backend *oss, MateMixerState state) -{ -    if (oss->priv->state == state) -        return; - -    oss->priv->state = state; - -    g_object_notify (G_OBJECT (oss), "state"); -} - -static gboolean -read_device (Oss4Backend *oss, gint index) -{ -    Oss4Device          *device; -    gboolean             ret; -    gchar               *description = NULL; -    struct oss_mixerinfo info; - -    /* We assume that the name and capabilities of a device do not change */ -    device = g_hash_table_lookup (oss->priv->devices, GINT_TO_POINTER (index)); -    if (G_UNLIKELY (device != NULL)) { -        g_debug ("Attempt to re-read already know device with index %d", index); -        return TRUE; -    } - -    info.dev = index; -    ret = ioctl (oss->priv->fd, SNDCTL_MIXERINFO, &info); -    if (ret == -1) { -        g_debug ("Failed to read mixer info: %s", g_strerror (errno)); -        return FALSE; -    } - -    if (info.enabled == 0) -        return TRUE; - -    /* Use the id as the device name and try to figure out the name of the -     * sound card from the sndstat file if it is available, otherwise use -     * the name from the mixer info */ -    if (oss->priv->sndstat != NULL && -        g_str_has_prefix (info.name, "pcm") == TRUE) -        description = read_device_sndstat_description (oss, info.name); - -    if (description == NULL) -        description = g_strdup (info.name); - -    device = oss4_device_new (info.id, description, oss->priv->fd, index); - -    ret = oss4_device_read (device); -    if (ret == TRUE) -        add_device (oss, device); - -    g_object_unref (device); -    g_free (description); - -    return ret; -} - -static gchar * -read_device_sndstat_description (Oss4Backend *oss, const gchar *prefix) -{ -    FILE  *fp; -    gchar  line[256]; -    gchar *description = NULL; - -    g_debug ("reading prefix %s", prefix); - -    fp = fopen (oss->priv->sndstat, "r"); -    if (fp == NULL) { -        g_warning ("Failed to read %s: %s", oss->priv->sndstat, g_strerror (errno)); -        return FALSE; -    } - -    while (fgets (line, sizeof (line), fp) != NULL) { -        gchar *p; - -        if (g_str_has_prefix (line, prefix) == FALSE) -            continue; - -        /* Example line: -         * pcm0: <ATI R6xx (HDMI)> (play) default */ -        p = strchr (line, '<'); -        if (p != NULL && *p && *(++p)) { -            gchar *end = strchr (p, '>'); - -            if (end != NULL) -                description = g_strndup (p, end - p); -        } - -        // XXX we can also read "default" at the end of the line -        // XXX http://ashish.is.lostca.se/2011/05/23/default-sound-device-in-freebsd/ -        if (g_str_has_suffix (line, "default") || -            g_str_has_suffix (line, "default)")) -            ; - -        if (description != NULL) -            break; -    } - -    fclose (fp); -    return description; -} - -static void -add_device (Oss4Backend *oss, Oss4Device *device) -{ -    MateMixerStream *stream; - -    /* Add device, file path is used as the key rather than the name, because -     * the name is not known until an OssDevice instance is created */ -    g_hash_table_insert (oss->priv->devices, -                         GINT_TO_POINTER (oss4_device_get_index (device)), -                         g_object_ref (device)); - -    g_signal_emit_by_name (G_OBJECT (oss), -                           "device-added", -                           mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); - -    /* Add streams if they exist */ -    stream = oss4_device_get_input_stream (device); -    if (stream != NULL) { -        g_hash_table_insert (oss->priv->streams, -                             g_strdup (mate_mixer_stream_get_name (stream)), -                             g_object_ref (stream)); - -        g_signal_emit_by_name (G_OBJECT (oss), -                               "stream-added", -                               mate_mixer_stream_get_name (stream)); -    } - -    stream = oss4_device_get_output_stream (device); -    if (stream != NULL) { -        g_hash_table_insert (oss->priv->streams, -                             g_strdup (mate_mixer_stream_get_name (stream)), -                             g_object_ref (stream)); - -        g_signal_emit_by_name (G_OBJECT (oss), -                               "stream-added", -                               mate_mixer_stream_get_name (stream)); -    } -} - -static void -remove_device (Oss4Backend *oss, Oss4Device *device) -{ -    MateMixerStream *stream; - -    /* Remove the device streams first as they are a part of the device */ -    stream = oss4_device_get_input_stream (device); -    if (stream != NULL) { -        const gchar *name = mate_mixer_stream_get_name (stream); - -        g_hash_table_remove (oss->priv->streams, name); -        g_signal_emit_by_name (G_OBJECT (oss), -                               "stream-removed", -                               name); -    } - -    stream = oss4_device_get_output_stream (device); -    if (stream != NULL) { -        const gchar *name = mate_mixer_stream_get_name (stream); - -        g_hash_table_remove (oss->priv->streams, stream); -        g_signal_emit_by_name (G_OBJECT (oss), -                               "stream-removed", -                               name); -    } - -    /* Remove the device */ -    g_object_ref (device); - -    g_hash_table_remove (oss->priv->devices, -                         GINT_TO_POINTER (oss4_device_get_index (device))); - -    g_signal_emit_by_name (G_OBJECT (oss), -                           "device-removed", -                           mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); - -    g_object_unref (device); -} diff --git a/backends/oss4/oss4-backend.h b/backends/oss4/oss4-backend.h deleted file mode 100644 index bc89e72..0000000 --- a/backends/oss4/oss4-backend.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef OSS4_BACKEND_H -#define OSS4_BACKEND_H - -#include <glib.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-backend-module.h> - -#define OSS4_TYPE_BACKEND                       \ -        (oss4_backend_get_type ()) -#define OSS4_BACKEND(o)                         \ -        (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS4_TYPE_BACKEND, Oss4Backend)) -#define OSS4_IS_BACKEND(o)                      \ -        (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS4_TYPE_BACKEND)) -#define OSS4_BACKEND_CLASS(k)                   \ -        (G_TYPE_CHECK_CLASS_CAST ((k), OSS4_TYPE_BACKEND, Oss4BackendClass)) -#define OSS4_IS_BACKEND_CLASS(k)                \ -        (G_TYPE_CHECK_CLASS_TYPE ((k), OSS4_TYPE_BACKEND)) -#define OSS4_BACKEND_GET_CLASS(o)               \ -        (G_TYPE_INSTANCE_GET_CLASS ((o), OSS4_TYPE_BACKEND, Oss4BackendClass)) - -typedef struct _Oss4Backend         Oss4Backend; -typedef struct _Oss4BackendClass    Oss4BackendClass; -typedef struct _Oss4BackendPrivate  Oss4BackendPrivate; - -struct _Oss4Backend -{ -    GObject parent; - -    /*< private >*/ -    Oss4BackendPrivate *priv; -}; - -struct _Oss4BackendClass -{ -    GObjectClass parent_class; -}; - -GType                       oss4_backend_get_type   (void) G_GNUC_CONST; - -/* Support function for dynamic loading of the backend module */ -void                        backend_module_init     (GTypeModule *module); -const MateMixerBackendInfo *backend_module_get_info (void); - -#endif /* OSS4_BACKEND_H */ diff --git a/backends/oss4/oss4-device.c b/backends/oss4/oss4-device.c deleted file mode 100644 index b68648b..0000000 --- a/backends/oss4/oss4-device.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include <errno.h> -#include <unistd.h> -#include <glib.h> -#include <glib/gi18n.h> -#include <glib/gstdio.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-port-private.h> -#include <libmatemixer/matemixer-stream.h> -#include <libmatemixer/matemixer-stream-control.h> - -#include "oss4-common.h" -#include "oss4-device.h" - -#define OSS4_DEVICE_ICON "audio-card" - -struct _Oss4DevicePrivate -{ -    gint             fd; -    gint             index; -    gchar           *name; -    gchar           *description; -    gchar           *icon; -    MateMixerStream *input; -    MateMixerStream *output; -}; - -enum { -    PROP_0, -    PROP_NAME, -    PROP_DESCRIPTION, -    PROP_ICON, -    PROP_ACTIVE_PROFILE, -    PROP_FD, -    PROP_INDEX -}; - -static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); - -static void oss4_device_class_init   (Oss4DeviceClass *klass); - -static void oss4_device_get_property (GObject        *object, -                                      guint           param_id, -                                      GValue         *value, -                                      GParamSpec     *pspec); -static void oss4_device_set_property (GObject        *object, -                                      guint           param_id, -                                      const GValue   *value, -                                      GParamSpec     *pspec); - -static void oss4_device_init         (Oss4Device     *device); -static void oss4_device_finalize     (GObject        *object); - -G_DEFINE_TYPE_WITH_CODE (Oss4Device, oss4_device, G_TYPE_OBJECT, -                         G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE, -                                                mate_mixer_device_interface_init)) - -static const gchar *oss4_device_get_name        (MateMixerDevice *device); -static const gchar *oss4_device_get_description (MateMixerDevice *device); -static const gchar *oss4_device_get_icon        (MateMixerDevice *device); - -static gboolean     read_mixer_devices          (Oss4Device      *device); - -static void -mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) -{ -    iface->get_name        = oss4_device_get_name; -    iface->get_description = oss4_device_get_description; -    iface->get_icon        = oss4_device_get_icon; -} - -static void -oss4_device_class_init (Oss4DeviceClass *klass) -{ -    GObjectClass *object_class; - -    object_class = G_OBJECT_CLASS (klass); -    object_class->finalize     = oss4_device_finalize; -    object_class->get_property = oss4_device_get_property; -    object_class->set_property = oss4_device_set_property; - -    g_object_class_install_property (object_class, -                                     PROP_FD, -                                     g_param_spec_int ("fd", -                                                       "File descriptor", -                                                       "File descriptor of the device", -                                                       G_MININT, -                                                       G_MAXINT, -                                                       -1, -                                                       G_PARAM_CONSTRUCT_ONLY | -                                                       G_PARAM_READWRITE | -                                                       G_PARAM_STATIC_STRINGS)); - -    g_object_class_install_property (object_class, -                                     PROP_INDEX, -                                     g_param_spec_int ("index", -                                                       "Index", -                                                       "Index of the device", -                                                       G_MININT, -                                                       G_MAXINT, -                                                       0, -                                                       G_PARAM_CONSTRUCT_ONLY | -                                                       G_PARAM_READWRITE | -                                                       G_PARAM_STATIC_STRINGS)); - -    g_object_class_override_property (object_class, PROP_NAME, "name"); -    g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); -    g_object_class_override_property (object_class, PROP_ICON, "icon"); -    g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile"); - -    g_type_class_add_private (object_class, sizeof (Oss4DevicePrivate)); -} - -static void -oss4_device_get_property (GObject    *object, -                         guint       param_id, -                         GValue     *value, -                         GParamSpec *pspec) -{ -    Oss4Device *device; - -    device = OSS4_DEVICE (object); - -    switch (param_id) { -    case PROP_NAME: -        g_value_set_string (value, device->priv->name); -        break; -    case PROP_DESCRIPTION: -        g_value_set_string (value, device->priv->description); -        break; -    case PROP_ICON: -        g_value_set_string (value, OSS4_DEVICE_ICON); -        break; -    case PROP_ACTIVE_PROFILE: -        /* Not supported */ -        g_value_set_object (value, NULL); -        break; -    case PROP_FD: -        g_value_set_int (value, device->priv->fd); -        break; -    case PROP_INDEX: -        g_value_set_int (value, device->priv->index); -        break; -    default: -        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); -        break; -    } -} - -static void -oss4_device_set_property (GObject      *object, -                         guint         param_id, -                         const GValue *value, -                         GParamSpec   *pspec) -{ -    Oss4Device *device; - -    device = OSS4_DEVICE (object); - -    switch (param_id) { -    case PROP_NAME: -        /* Construct-only string */ -        device->priv->name = g_strdup (g_value_get_string (value)); -        break; -    case PROP_DESCRIPTION: -        /* Construct-only string */ -        device->priv->description = g_strdup (g_value_get_string (value)); -        break; -    case PROP_ICON: -        /* Construct-only string */ -        device->priv->icon = g_strdup (g_value_get_string (value)); -        break; -    case PROP_FD: -        device->priv->fd = dup (g_value_get_int (value)); -        break; -    case PROP_INDEX: -        device->priv->index = g_value_get_int (value); -        break; -    default: -        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); -        break; -    } -} - -static void -oss4_device_init (Oss4Device *device) -{ -    device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, -                                                OSS4_TYPE_DEVICE, -                                                Oss4DevicePrivate); -} - -static void -oss4_device_finalize (GObject *object) -{ -    Oss4Device *device; - -    device = OSS4_DEVICE (object); - -    g_free (device->priv->name); -    g_free (device->priv->description); - -    if (device->priv->fd != -1) -        g_close (device->priv->fd, NULL); - -    G_OBJECT_CLASS (oss4_device_parent_class)->finalize (object); -} - -Oss4Device * -oss4_device_new (const gchar *name, -                 const gchar *description, -                 gint         fd, -                 gint         index) -{ -    Oss4Device *device; - -    device = g_object_new (OSS4_TYPE_DEVICE, -                           "name", name, -                           "description", description, -                           "fd", fd, -                           "index", index, -                           NULL); - -    return device; -} - -gboolean -oss4_device_read (Oss4Device *odevice) -{ -    gint exts; -    gint ret; -    gint i; - -    ret = ioctl (odevice->priv->fd, SNDCTL_MIX_NREXT, &exts); -    if (ret == -1) -        return FALSE; - -    for (i = 0; i < exts; i++) { -        oss_mixext ext; - -        ext.dev  = odevice->priv->index; -        ext.ctrl = i; -        ret = ioctl (odevice->priv->fd, SNDCTL_MIX_EXTINFO, &ext); -        if (ret == -1) -            continue; - -        g_debug ("Mixer control %d type %d\n" -                 " min %d max %d\n" -                 " id %s\n" -                 " extname %s", -                 i,ext.type, ext.minvalue, ext.maxvalue, ext.id, ext.extname); -    } - -    return TRUE; -} - -gint -oss4_device_get_index (Oss4Device *odevice) -{ -    g_return_val_if_fail (OSS4_IS_DEVICE (odevice), FALSE); - -    return odevice->priv->index; -} - -MateMixerStream * -oss4_device_get_input_stream (Oss4Device *odevice) -{ -    g_return_val_if_fail (OSS4_IS_DEVICE (odevice), FALSE); - -    return odevice->priv->input; -} - -MateMixerStream * -oss4_device_get_output_stream (Oss4Device *odevice) -{ -    g_return_val_if_fail (OSS4_IS_DEVICE (odevice), FALSE); - -    return odevice->priv->output; -} - -static gboolean -read_mixer_devices (Oss4Device *device) -{ -} - -static const gchar * -oss4_device_get_name (MateMixerDevice *device) -{ -    g_return_val_if_fail (OSS4_IS_DEVICE (device), NULL); - -    return OSS4_DEVICE (device)->priv->name; -} - -static const gchar * -oss4_device_get_description (MateMixerDevice *device) -{ -    g_return_val_if_fail (OSS4_IS_DEVICE (device), NULL); - -    return OSS4_DEVICE (device)->priv->description; -} - -static const gchar * -oss4_device_get_icon (MateMixerDevice *device) -{ -    g_return_val_if_fail (OSS4_IS_DEVICE (device), NULL); - -    return OSS4_DEVICE_ICON; -} diff --git a/backends/oss4/oss4-device.h b/backends/oss4/oss4-device.h deleted file mode 100644 index 3ec72e7..0000000 --- a/backends/oss4/oss4-device.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef OSS4_DEVICE_H -#define OSS4_DEVICE_H - -#include <glib.h> -#include <glib-object.h> - -G_BEGIN_DECLS - -#define OSS4_TYPE_DEVICE                        \ -        (oss4_device_get_type ()) -#define OSS4_DEVICE(o)                          \ -        (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS4_TYPE_DEVICE, Oss4Device)) -#define OSS4_IS_DEVICE(o)                       \ -        (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS4_TYPE_DEVICE)) -#define OSS4_DEVICE_CLASS(k)                    \ -        (G_TYPE_CHECK_CLASS_CAST ((k), OSS4_TYPE_DEVICE, Oss4DeviceClass)) -#define OSS4_IS_DEVICE_CLASS(k)                 \ -        (G_TYPE_CHECK_CLASS_TYPE ((k), OSS4_TYPE_DEVICE)) -#define OSS4_DEVICE_GET_CLASS(o)                \ -        (G_TYPE_INSTANCE_GET_CLASS ((o), OSS4_TYPE_DEVICE, Oss4DeviceClass)) - -typedef struct _Oss4Device         Oss4Device; -typedef struct _Oss4DeviceClass    Oss4DeviceClass; -typedef struct _Oss4DevicePrivate  Oss4DevicePrivate; - -struct _Oss4Device -{ -    GObject parent; - -    /*< private >*/ -    Oss4DevicePrivate *priv; -}; - -struct _Oss4DeviceClass -{ -    GObjectClass parent; -}; - -GType            oss4_device_get_type          (void) G_GNUC_CONST; - -Oss4Device *     oss4_device_new (const gchar *name, -                                  const gchar *description, -                                  gint         fd, -                                  gint         index); - -gboolean         oss4_device_read              (Oss4Device   *device); - -gint             oss4_device_get_index         (Oss4Device *odevice); - -MateMixerStream *oss4_device_get_input_stream  (Oss4Device   *odevice); -MateMixerStream *oss4_device_get_output_stream (Oss4Device   *odevice); - -G_END_DECLS - -#endif /* OSS_DEVICE_H */ | 
