/* * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com> * * 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 <string.h> #include <glib.h> #include <glib/gi18n.h> #include <glib-object.h> #include <libmatemixer/matemixer.h> #include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" #include "pulse-device.h" #include "pulse-device-profile.h" #include "pulse-device-switch.h" #include "pulse-port.h" #include "pulse-stream.h" struct _PulseDevicePrivate { guint32 index; GHashTable *ports; GHashTable *streams; GList *streams_list; PulseConnection *connection; PulseDeviceSwitch *pswitch; GList *pswitch_list; }; enum { PROP_0, PROP_INDEX, PROP_CONNECTION, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES] = { NULL, }; static void pulse_device_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); static void pulse_device_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); static void pulse_device_dispose (GObject *object); static void pulse_device_finalize (GObject *object); G_DEFINE_TYPE_WITH_PRIVATE (PulseDevice, pulse_device, MATE_MIXER_TYPE_DEVICE) static MateMixerStream *pulse_device_get_stream (MateMixerDevice *mmd, const gchar *name); static const GList * pulse_device_list_streams (MateMixerDevice *mmd); static const GList * pulse_device_list_switches (MateMixerDevice *mmd); static void pulse_device_load (PulseDevice *device, const pa_card_info *info); static void free_list_streams (PulseDevice *device); static void pulse_device_class_init (PulseDeviceClass *klass) { GObjectClass *object_class; MateMixerDeviceClass *device_class; object_class = G_OBJECT_CLASS (klass); object_class->dispose = pulse_device_dispose; object_class->finalize = pulse_device_finalize; object_class->get_property = pulse_device_get_property; object_class->set_property = pulse_device_set_property; device_class = MATE_MIXER_DEVICE_CLASS (klass); device_class->get_stream = pulse_device_get_stream; device_class->list_streams = pulse_device_list_streams; device_class->list_switches = pulse_device_list_switches; properties[PROP_INDEX] = g_param_spec_uint ("index", "Index", "Index of the device", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); properties[PROP_CONNECTION] = g_param_spec_object ("connection", "Connection", "PulseAudio connection", PULSE_TYPE_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPERTIES, properties); } static void pulse_device_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { PulseDevice *device; device = PULSE_DEVICE (object); switch (param_id) { case PROP_INDEX: g_value_set_uint (value, device->priv->index); break; case PROP_CONNECTION: g_value_set_object (value, device->priv->connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void pulse_device_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { PulseDevice *device; device = PULSE_DEVICE (object); switch (param_id) { case PROP_INDEX: device->priv->index = g_value_get_uint (value); break; case PROP_CONNECTION: device->priv->connection = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void pulse_device_init (PulseDevice *device) { device->priv = pulse_device_get_instance_private (device); device->priv->ports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); device->priv->streams = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } static void pulse_device_dispose (GObject *object) { PulseDevice *device; device = PULSE_DEVICE (object); g_hash_table_remove_all (device->priv->ports); g_hash_table_remove_all (device->priv->streams); g_clear_object (&device->priv->connection); g_clear_object (&device->priv->pswitch); free_list_streams (device); if (device->priv->pswitch_list != NULL) { g_list_free (device->priv->pswitch_list); device->priv->pswitch_list = NULL; } G_OBJECT_CLASS (pulse_device_parent_class)->dispose (object); } static void pulse_device_finalize (GObject *object) { PulseDevice *device; device = PULSE_DEVICE (object); g_hash_table_unref (device->priv->ports); g_hash_table_unref (device->priv->streams); G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object); } PulseDevice * pulse_device_new (PulseConnection *connection, const pa_card_info *info) { PulseDevice *device; const gchar *label; const gchar *icon; g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); g_return_val_if_fail (info != NULL, NULL); label = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION); if G_UNLIKELY (label == NULL) label = info->name; icon = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME); if G_UNLIKELY (icon == NULL) icon = "audio-card"; /* Consider the device index as unchanging parameter */ device = g_object_new (PULSE_TYPE_DEVICE, "index", info->index, "connection", connection, "name", info->name, "label", label, "icon", icon, NULL); pulse_device_load (device, info); pulse_device_update (device, info); return device; } void pulse_device_update (PulseDevice *device, const pa_card_info *info) { g_return_if_fail (PULSE_IS_DEVICE (device)); g_return_if_fail (info != NULL); if G_LIKELY (info->active_profile2 != NULL) pulse_device_switch_set_active_profile_by_name (device->priv->pswitch, info->active_profile2->name); } void pulse_device_add_stream (PulseDevice *device, PulseStream *stream) { const gchar *name; g_return_if_fail (PULSE_IS_DEVICE (device)); g_return_if_fail (PULSE_IS_STREAM (stream)); name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); g_hash_table_insert (device->priv->streams, g_strdup (name), g_object_ref (stream)); free_list_streams (device); g_signal_emit_by_name (G_OBJECT (device), "stream-added", name); } void pulse_device_remove_stream (PulseDevice *device, PulseStream *stream) { const gchar *name; g_return_if_fail (PULSE_IS_DEVICE (device)); g_return_if_fail (PULSE_IS_STREAM (stream)); name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); free_list_streams (device); g_hash_table_remove (device->priv->streams, name); g_signal_emit_by_name (G_OBJECT (device), "stream-removed", name); } guint32 pulse_device_get_index (PulseDevice *device) { g_return_val_if_fail (PULSE_IS_DEVICE (device), 0); return device->priv->index; } PulseConnection * pulse_device_get_connection (PulseDevice *device) { g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); return device->priv->connection; } PulsePort * pulse_device_get_port (PulseDevice *device, const gchar *name) { g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); g_return_val_if_fail (name != NULL, NULL); return g_hash_table_lookup (device->priv->ports, name); } static MateMixerStream * pulse_device_get_stream (MateMixerDevice *mmd, const gchar *name) { g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL); g_return_val_if_fail (name != NULL, NULL); return g_hash_table_lookup (PULSE_DEVICE (mmd)->priv->streams, name); } static const GList * pulse_device_list_streams (MateMixerDevice *mmd) { PulseDevice *device; g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL); device = PULSE_DEVICE (mmd); if (device->priv->streams_list == NULL) { device->priv->streams_list = g_hash_table_get_values (device->priv->streams); if (device->priv->streams_list != NULL) g_list_foreach (device->priv->streams_list, (GFunc) g_object_ref, NULL); } return device->priv->streams_list; } static const GList * pulse_device_list_switches (MateMixerDevice *mmd) { g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL); return PULSE_DEVICE (mmd)->priv->pswitch_list; } static void pulse_device_load (PulseDevice *device, const pa_card_info *info) { guint i; for (i = 0; i < info->n_ports; i++) { PulsePort *port; const gchar *name; const gchar *icon; name = info->ports[i]->name; icon = pa_proplist_gets (info->ports[i]->proplist, "device.icon_name"); port = pulse_port_new (name, info->ports[i]->description, icon, info->ports[i]->priority); g_hash_table_insert (device->priv->ports, g_strdup (name), port); } /* Create the device profile switch */ if (info->n_profiles > 0) { device->priv->pswitch = pulse_device_switch_new ("profile", _("Profile"), device); device->priv->pswitch_list = g_list_prepend (NULL, device->priv->pswitch); } for (i = 0; i < info->n_profiles; i++) { PulseDeviceProfile *profile; pa_card_profile_info2 *p_info = info->profiles2[i]; /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only * differs in the new available flag, we use it not to include profiles * which are unavailable */ if (p_info->available == 0) continue; profile = pulse_device_profile_new (p_info->name, p_info->description, p_info->priority); pulse_device_switch_add_profile (device->priv->pswitch, profile); g_object_unref (profile); } } static void free_list_streams (PulseDevice *device) { if (device->priv->streams_list == NULL) return; g_list_free_full (device->priv->streams_list, g_object_unref); device->priv->streams_list = NULL; }