diff options
Diffstat (limited to 'backends/pulse/pulse-stream.c')
-rw-r--r-- | backends/pulse/pulse-stream.c | 885 |
1 files changed, 793 insertions, 92 deletions
diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c index a9e01d1..529f2c9 100644 --- a/backends/pulse/pulse-stream.c +++ b/backends/pulse/pulse-stream.c @@ -17,69 +17,178 @@ #include <glib.h> #include <glib-object.h> +#include <string.h> +#include <libmatemixer/matemixer-device.h> +#include <libmatemixer/matemixer-enums.h> #include <libmatemixer/matemixer-stream.h> #include <libmatemixer/matemixer-port.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" +#include "pulse-helpers.h" #include "pulse-stream.h" -struct _MateMixerPulseStreamPrivate -{ - guint32 index; - gchar *name; - gchar *description; - gchar *icon; - guint channels; - gboolean mute; - GList *ports; - MateMixerPort *port; - MateMixerPulseConnection *connection; +struct _PulseStreamPrivate +{ + guint32 index; + guint32 index_device; + gchar *name; + gchar *description; + gchar *icon; + MateMixerDevice *device; + MateMixerStreamFlags flags; + MateMixerStreamStatus status; + gboolean mute; + pa_cvolume volume; + pa_volume_t volume_base; + guint32 volume_steps; + pa_channel_map channel_map; + gdouble balance; + gdouble fade; + GList *ports; + MateMixerPort *port; + PulseConnection *connection; }; enum { PROP_0, - PROP_INDEX, PROP_NAME, PROP_DESCRIPTION, PROP_ICON, - PROP_CHANNELS, - PROP_VOLUME, + PROP_DEVICE, + PROP_FLAGS, + PROP_STATUS, PROP_MUTE, + PROP_NUM_CHANNELS, + PROP_VOLUME, + PROP_VOLUME_DB, + PROP_BALANCE, + PROP_FADE, + PROP_ACTIVE_PORT, + PROP_INDEX, + PROP_CONNECTION, N_PROPERTIES }; -static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); -static void mate_mixer_pulse_stream_class_init (MateMixerPulseStreamClass *klass); -static void mate_mixer_pulse_stream_init (MateMixerPulseStream *stream); -static void mate_mixer_pulse_stream_dispose (GObject *object); -static void mate_mixer_pulse_stream_finalize (GObject *object); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MateMixerPulseStream, mate_mixer_pulse_stream, G_TYPE_OBJECT, +static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); +static void pulse_stream_class_init (PulseStreamClass *klass); +static void pulse_stream_init (PulseStream *stream); +static void pulse_stream_dispose (GObject *object); +static void pulse_stream_finalize (GObject *object); + +/* Interface implementation */ +static const gchar * stream_get_name (MateMixerStream *stream); +static const gchar * stream_get_description (MateMixerStream *stream); +static const gchar * stream_get_icon (MateMixerStream *stream); +static MateMixerDevice * stream_get_device (MateMixerStream *stream); +static MateMixerStreamFlags stream_get_flags (MateMixerStream *stream); +static MateMixerStreamStatus stream_get_status (MateMixerStream *stream); +static gboolean stream_get_mute (MateMixerStream *stream); +static gboolean stream_set_mute (MateMixerStream *stream, + gboolean mute); +static guint stream_get_num_channels (MateMixerStream *stream); +static gint64 stream_get_volume (MateMixerStream *stream); +static gboolean stream_set_volume (MateMixerStream *stream, + gint64 volume); +static gdouble stream_get_volume_db (MateMixerStream *stream); +static gboolean stream_set_volume_db (MateMixerStream *stream, + gdouble volume_db); +static MateMixerChannelPosition stream_get_channel_position (MateMixerStream *stream, + guint channel); +static gint64 stream_get_channel_volume (MateMixerStream *stream, + guint channel); +static gboolean stream_set_channel_volume (MateMixerStream *stream, + guint channel, + gint64 volume); +static gdouble stream_get_channel_volume_db (MateMixerStream *stream, + guint channel); +static gboolean stream_set_channel_volume_db (MateMixerStream *stream, + guint channel, + gdouble volume_db); +static gboolean stream_has_position (MateMixerStream *stream, + MateMixerChannelPosition position); +static gint64 stream_get_position_volume (MateMixerStream *stream, + MateMixerChannelPosition position); +static gboolean stream_set_position_volume (MateMixerStream *stream, + MateMixerChannelPosition position, + gint64 volume); +static gdouble stream_get_position_volume_db (MateMixerStream *stream, + MateMixerChannelPosition position); +static gboolean stream_set_position_volume_db (MateMixerStream *stream, + MateMixerChannelPosition position, + gdouble volume_db); +static gdouble stream_get_balance (MateMixerStream *stream); +static gboolean stream_set_balance (MateMixerStream *stream, + gdouble balance); +static gdouble stream_get_fade (MateMixerStream *stream); +static gboolean stream_set_fade (MateMixerStream *stream, + gdouble fade); +static gboolean stream_suspend (MateMixerStream *stream); +static gboolean stream_resume (MateMixerStream *stream); +static const GList * stream_list_ports (MateMixerStream *stream); +static MateMixerPort * stream_get_active_port (MateMixerStream *stream); +static gboolean stream_set_active_port (MateMixerStream *stream, + const gchar *port); +static gint64 stream_get_min_volume (MateMixerStream *stream); +static gint64 stream_get_max_volume (MateMixerStream *stream); +static gint64 stream_get_normal_volume (MateMixerStream *stream); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM, mate_mixer_stream_interface_init)) static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) { - iface->get_name = mate_mixer_pulse_stream_get_name; - iface->get_description = mate_mixer_pulse_stream_get_description; - iface->get_icon = mate_mixer_pulse_stream_get_icon; - iface->list_ports = mate_mixer_pulse_stream_list_ports; + iface->get_name = stream_get_name; + iface->get_description = stream_get_description; + iface->get_icon = stream_get_icon; + iface->get_device = stream_get_device; + iface->get_flags = stream_get_flags; + iface->get_status = stream_get_status; + iface->get_mute = stream_get_mute; + iface->set_mute = stream_set_mute; + iface->get_num_channels = stream_get_num_channels; + iface->get_volume = stream_get_volume; + iface->set_volume = stream_set_volume; + iface->get_volume_db = stream_get_volume_db; + iface->set_volume_db = stream_set_volume_db; + iface->get_channel_position = stream_get_channel_position; + iface->get_channel_volume = stream_get_channel_volume; + iface->set_channel_volume = stream_set_channel_volume; + iface->get_channel_volume_db = stream_get_channel_volume_db; + iface->set_channel_volume_db = stream_set_channel_volume_db; + iface->has_position = stream_has_position; + iface->get_position_volume = stream_get_position_volume; + iface->set_position_volume = stream_set_position_volume; + iface->get_position_volume_db = stream_get_position_volume_db; + iface->set_position_volume_db = stream_set_position_volume_db; + iface->get_balance = stream_get_balance; + iface->set_balance = stream_set_balance; + iface->get_fade = stream_get_fade; + iface->set_fade = stream_set_fade; + iface->suspend = stream_suspend; + iface->resume = stream_resume; + iface->list_ports = stream_list_ports; + iface->get_active_port = stream_get_active_port; + iface->set_active_port = stream_set_active_port; + iface->get_min_volume = stream_get_min_volume; + iface->get_max_volume = stream_get_max_volume; + iface->get_normal_volume = stream_get_normal_volume; } static void -mate_mixer_pulse_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) +pulse_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) { - MateMixerPulseStream *stream; + PulseStream *stream; - stream = MATE_MIXER_PULSE_STREAM (object); + stream = PULSE_STREAM (object); switch (param_id) { case PROP_NAME: @@ -91,6 +200,42 @@ mate_mixer_pulse_stream_get_property (GObject *object, case PROP_ICON: g_value_set_string (value, stream->priv->icon); break; + case PROP_DEVICE: + g_value_set_object (value, stream->priv->device); + break; + case PROP_FLAGS: + g_value_set_flags (value, stream->priv->flags); + break; + case PROP_STATUS: + g_value_set_enum (value, stream->priv->status); + break; + case PROP_MUTE: + g_value_set_boolean (value, stream->priv->mute); + break; + case PROP_NUM_CHANNELS: + g_value_set_uint (value, stream_get_num_channels (MATE_MIXER_STREAM (stream))); + break; + case PROP_VOLUME: + g_value_set_int64 (value, stream_get_volume (MATE_MIXER_STREAM (stream))); + break; + case PROP_VOLUME_DB: + g_value_set_double (value, stream_get_volume_db (MATE_MIXER_STREAM (stream))); + break; + case PROP_BALANCE: + g_value_set_double (value, stream->priv->balance); + break; + case PROP_FADE: + g_value_set_double (value, stream->priv->fade); + break; + case PROP_ACTIVE_PORT: + g_value_set_object (value, stream->priv->port); + break; + case PROP_INDEX: + g_value_set_uint (value, stream->priv->index); + break; + case PROP_CONNECTION: + g_value_set_object (value, stream->priv->connection); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -98,18 +243,18 @@ mate_mixer_pulse_stream_get_property (GObject *object, } static void -mate_mixer_pulse_stream_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) +pulse_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) { - MateMixerPulseStream *stream; + PulseStream *stream; - stream = MATE_MIXER_PULSE_STREAM (object); + stream = PULSE_STREAM (object); switch (param_id) { case PROP_NAME: - stream->priv->name = g_strdup (g_value_get_string (value)); + stream->priv->name = g_strdup (g_value_dup_string (value)); break; case PROP_DESCRIPTION: stream->priv->description = g_strdup (g_value_get_string (value)); @@ -117,6 +262,28 @@ mate_mixer_pulse_stream_set_property (GObject *object, case PROP_ICON: stream->priv->icon = g_strdup (g_value_get_string (value)); break; + case PROP_DEVICE: + // XXX may be NULL and the device may become known after the stream, + // figure this out.. + // stream->priv->device = g_object_ref (g_value_get_object (value)); + break; + case PROP_FLAGS: + stream->priv->flags = g_value_get_flags (value); + break; + case PROP_STATUS: + stream->priv->status = g_value_get_enum (value); + break; + case PROP_MUTE: + stream->priv->mute = g_value_get_boolean (value); + break; + case PROP_INDEX: + stream->priv->index = g_value_get_uint (value); + break; + case PROP_CONNECTION: + stream->priv->connection = g_value_dup_object (value); + break; + case PROP_ACTIVE_PORT: + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -124,136 +291,670 @@ mate_mixer_pulse_stream_set_property (GObject *object, } static void -mate_mixer_pulse_stream_class_init (MateMixerPulseStreamClass *klass) +pulse_stream_class_init (PulseStreamClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = mate_mixer_pulse_stream_dispose; - object_class->finalize = mate_mixer_pulse_stream_finalize; - object_class->get_property = mate_mixer_pulse_stream_get_property; - object_class->set_property = mate_mixer_pulse_stream_set_property; + object_class->dispose = pulse_stream_dispose; + object_class->finalize = pulse_stream_finalize; + object_class->get_property = pulse_stream_get_property; + object_class->set_property = pulse_stream_set_property; + + g_object_class_install_property (object_class, + PROP_INDEX, + g_param_spec_uint ("index", + "Index", + "Stream index", + 0, + G_MAXUINT, + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_CONNECTION, + g_param_spec_object ("connection", + "Connection", + "PulseAudio connection", + PULSE_TYPE_CONNECTION, + 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_type_class_add_private (object_class, sizeof (MateMixerPulseStreamPrivate)); + 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_STATUS, "status"); + g_object_class_override_property (object_class, PROP_MUTE, "mute"); + g_object_class_override_property (object_class, PROP_NUM_CHANNELS, "num-channels"); + g_object_class_override_property (object_class, PROP_VOLUME, "volume"); + g_object_class_override_property (object_class, PROP_VOLUME_DB, "volume-db"); + g_object_class_override_property (object_class, PROP_BALANCE, "balance"); + g_object_class_override_property (object_class, PROP_FADE, "fade"); + g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port"); + + g_type_class_add_private (object_class, sizeof (PulseStreamPrivate)); } static void -mate_mixer_pulse_stream_init (MateMixerPulseStream *stream) +pulse_stream_init (PulseStream *stream) { - stream->priv = G_TYPE_INSTANCE_GET_PRIVATE ( - stream, - MATE_MIXER_TYPE_PULSE_STREAM, - MateMixerPulseStreamPrivate); + stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, + PULSE_TYPE_STREAM, + PulseStreamPrivate); } static void -mate_mixer_pulse_stream_dispose (GObject *object) +pulse_stream_dispose (GObject *object) { - MateMixerPulseStream *stream; + PulseStream *stream; - stream = MATE_MIXER_PULSE_STREAM (object); + stream = PULSE_STREAM (object); if (stream->priv->ports) { g_list_free_full (stream->priv->ports, g_object_unref); stream->priv->ports = NULL; } + + g_clear_object (&stream->priv->port); + g_clear_object (&stream->priv->device); g_clear_object (&stream->priv->connection); - G_OBJECT_CLASS (mate_mixer_pulse_stream_parent_class)->dispose (object); + G_OBJECT_CLASS (pulse_stream_parent_class)->dispose (object); } static void -mate_mixer_pulse_stream_finalize (GObject *object) +pulse_stream_finalize (GObject *object) { - MateMixerPulseStream *stream; + PulseStream *stream; - stream = MATE_MIXER_PULSE_STREAM (object); + stream = PULSE_STREAM (object); g_free (stream->priv->name); g_free (stream->priv->description); g_free (stream->priv->icon); - G_OBJECT_CLASS (mate_mixer_pulse_stream_parent_class)->finalize (object); + G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object); +} + +guint32 +pulse_stream_get_index (PulseStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return stream->priv->index; } -MateMixerPulseConnection * -mate_mixer_pulse_stream_get_connection (MateMixerPulseStream *stream) +PulseConnection * +pulse_stream_get_connection (PulseStream *stream) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), NULL); + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); return stream->priv->connection; } -guint32 -mate_mixer_pulse_stream_get_index (MateMixerPulseStream *stream) +gboolean +pulse_stream_update_name (PulseStream *stream, const gchar *name) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return stream->priv->index; + /* Allow the name to be NULL */ + if (g_strcmp0 (name, stream->priv->name)) { + g_free (stream->priv->name); + stream->priv->name = g_strdup (name); + + g_object_notify (G_OBJECT (stream), "name"); + } + return TRUE; +} + +gboolean +pulse_stream_update_description (PulseStream *stream, const gchar *description) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + /* Allow the description to be NULL */ + if (g_strcmp0 (description, stream->priv->description)) { + g_free (stream->priv->description); + stream->priv->description = g_strdup (description); + + g_object_notify (G_OBJECT (stream), "description"); + } + return TRUE; } -const gchar * -mate_mixer_pulse_stream_get_name (MateMixerStream *stream) +gboolean +pulse_stream_update_flags (PulseStream *stream, MateMixerStreamFlags flags) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM (stream)->priv->name; + if (stream->priv->flags != flags) { + stream->priv->flags = flags; + g_object_notify (G_OBJECT (stream), "flags"); + } + return TRUE; } -const gchar * -mate_mixer_pulse_stream_get_description (MateMixerStream *stream) +gboolean +pulse_stream_update_status (PulseStream *stream, MateMixerStreamStatus status) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM (stream)->priv->description; + if (stream->priv->status != status) { + stream->priv->status = status; + g_object_notify (G_OBJECT (stream), "status"); + } + return TRUE; } -const gchar * -mate_mixer_pulse_stream_get_icon (MateMixerStream *stream) +gboolean +pulse_stream_update_mute (PulseStream *stream, gboolean mute) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM (stream)->priv->icon; + if (stream->priv->mute != mute) { + stream->priv->mute = mute; + g_object_notify (G_OBJECT (stream), "mute"); + } + return TRUE; } -MateMixerPort * -mate_mixer_pulse_stream_get_active_port (MateMixerStream *stream) +gboolean +pulse_stream_update_volume (PulseStream *stream, const pa_cvolume *volume) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM (stream)->priv->port; + if (!pa_cvolume_equal (&stream->priv->volume, volume)) { + stream->priv->volume = *volume; + + g_object_notify (G_OBJECT (stream), "volume"); + + // XXX probably should notify about volume-db too but the flags may + // be known later + } + return TRUE; } -const GList * -mate_mixer_pulse_stream_list_ports (MateMixerStream *stream) +gboolean +pulse_stream_update_volume_extended (PulseStream *stream, + const pa_cvolume *volume, + pa_volume_t volume_base, + guint32 volume_steps) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + // XXX use volume_base and volume_steps + + if (!pa_cvolume_equal (&stream->priv->volume, volume)) { + stream->priv->volume = *volume; + + g_object_notify (G_OBJECT (stream), "volume"); - return MATE_MIXER_PULSE_STREAM (stream)->priv->ports; + // XXX probably should notify about volume-db too but the flags may + // be known later + } + + stream->priv->volume_base = volume_base; + stream->priv->volume_steps = volume_steps; + return TRUE; } gboolean -mate_mixer_pulse_stream_set_volume (MateMixerStream *stream, guint32 volume) +pulse_stream_update_channel_map (PulseStream *stream, const pa_channel_map *map) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, volume); + if (!pa_channel_map_equal (&stream->priv->channel_map, map)) + stream->priv->channel_map = *map; + + return TRUE; } gboolean -mate_mixer_pulse_stream_set_mute (MateMixerStream *stream, gboolean mute) +pulse_stream_update_ports (PulseStream *stream, GList *ports) { - g_return_val_if_fail (MATE_MIXER_IS_PULSE_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return MATE_MIXER_PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute); + /* Right now we do not change the list of ports during update */ + g_warn_if_fail (stream->priv->ports == NULL); + + stream->priv->ports = ports; + return TRUE; } gboolean -mate_mixer_pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) +pulse_stream_update_active_port (PulseStream *stream, const gchar *port_name) { + GList *list; + MateMixerPort *port = NULL; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + list = stream->priv->ports; + while (list) { + port = MATE_MIXER_PORT (list->data); + + if (!g_strcmp0 (mate_mixer_port_get_name (port), port_name)) + break; + + port = NULL; + list = list->next; + } + + if (stream->priv->port != port) { + if (stream->priv->port) + g_clear_object (&stream->priv->port); + if (port) + stream->priv->port = g_object_ref (port); + + g_object_notify (G_OBJECT (stream), "active-port"); + } return TRUE; } + +static const gchar * +stream_get_name (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->name; +} + +static const gchar * +stream_get_description (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->description; +} + +static const gchar * +stream_get_icon (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->icon; +} + +static MateMixerDevice * +stream_get_device (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->device; +} + +static MateMixerStreamFlags +stream_get_flags (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->flags; +} + +static MateMixerStreamStatus +stream_get_status (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->status; +} + +static gboolean +stream_get_mute (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->mute; +} + +static gboolean +stream_set_mute (MateMixerStream *stream, gboolean mute) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + ps = PULSE_STREAM (stream); + + if (ps->priv->mute == mute) + return TRUE; + + return PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute); +} + +static guint +stream_get_num_channels (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->volume.channels; +} + +static gint64 +stream_get_volume (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return (gint64) pa_cvolume_max (&PULSE_STREAM (stream)->priv->volume); +} + +static gboolean +stream_set_volume (MateMixerStream *stream, gint64 volume) +{ + pa_cvolume cvolume; + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + ps = PULSE_STREAM (stream); + cvolume = ps->priv->volume; + + if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) { + g_warning ("Invalid PulseAudio volume value %" G_GINT64_FORMAT, volume); + return FALSE; + } + + /* This is the only function which passes a volume request to the real class, so + * all the pa_cvolume validations are only done here */ + + + + return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_volume_db (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + + if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) + return FALSE; + + return pa_sw_volume_to_dB (stream_get_volume (stream)); +} + +static gboolean +stream_set_volume_db (MateMixerStream *stream, gdouble volume_db) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) + return FALSE; + + return stream_set_volume (stream, pa_sw_volume_from_dB (volume_db)); +} + +static MateMixerChannelPosition +stream_get_channel_position (MateMixerStream *stream, guint channel) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + ps = PULSE_STREAM (stream); + + if (channel >= ps->priv->channel_map.channels) { + g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name); + return MATE_MIXER_CHANNEL_UNKNOWN_POSITION; + } + return pulse_convert_position_to_pulse (ps->priv->channel_map.map[channel]); +} + +static gint64 +stream_get_channel_volume (MateMixerStream *stream, guint channel) +{ + PulseStream *ps; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + ps = PULSE_STREAM (stream); + + if (channel >= ps->priv->volume.channels) { + g_warning ("Invalid channel %u of stream %s", channel, ps->priv->name); + return stream_get_min_volume (stream); + } + return (gint64) ps->priv->volume.values[channel]; +} + +static gboolean +stream_set_channel_volume (MateMixerStream *stream, guint channel, gint64 volume) +{ + pa_cvolume cvolume; + PulseStream *pstream; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + pstream = PULSE_STREAM (stream); + cvolume = pstream->priv->volume; + + if (channel >= pstream->priv->volume.channels) { + g_warning ("Invalid channel %u of stream %s", channel, pstream->priv->name); + return FALSE; + } + + cvolume.values[channel] = (pa_volume_t) volume; + + if (!pa_cvolume_valid (&cvolume)) { + g_warning ("Invalid PulseAudio volume value %" G_GINT64_FORMAT, volume); + return FALSE; + } + return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_channel_volume_db (MateMixerStream *stream, guint channel) +{ + PulseStream *pstream; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + + pstream = PULSE_STREAM (stream); + + if (channel >= pstream->priv->volume.channels) { + g_warning ("Invalid channel %u of stream %s", channel, pstream->priv->name); + return 0.0; + } + return pa_sw_volume_to_dB (pstream->priv->volume.values[channel]); +} + +static gboolean +stream_set_channel_volume_db (MateMixerStream *stream, + guint channel, + gdouble volume_db) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return stream_set_channel_volume (stream, + channel, + pa_sw_volume_from_dB (volume_db)); +} + +static gboolean +stream_has_position (MateMixerStream *stream, MateMixerChannelPosition position) +{ + PulseStreamPrivate *priv; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + priv = PULSE_STREAM (stream)->priv; + + return pa_channel_map_has_position (&priv->channel_map, + pulse_convert_position_to_pulse (position)); +} + +static gint64 +stream_get_position_volume (MateMixerStream *stream, + MateMixerChannelPosition position) +{ + PulseStreamPrivate *priv; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + + priv = PULSE_STREAM (stream)->priv; + + return pa_cvolume_get_position (&priv->volume, + &priv->channel_map, + pulse_convert_position_to_pulse (position)); +} + +static gboolean +stream_set_position_volume (MateMixerStream *stream, + MateMixerChannelPosition position, + gint64 volume) +{ + PulseStreamPrivate *priv; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + priv = PULSE_STREAM (stream)->priv; + cvolume = priv->volume; + + if (!pa_cvolume_set_position (&cvolume, + &priv->channel_map, + pulse_convert_position_to_pulse (position), + (pa_volume_t) volume)) { + return FALSE; + } + return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_position_volume_db (MateMixerStream *stream, + MateMixerChannelPosition position) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + + return pa_sw_volume_to_dB (stream_get_position_volume (stream, position)); +} + +static gboolean +stream_set_position_volume_db (MateMixerStream *stream, + MateMixerChannelPosition position, + gdouble volume_db) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return stream_set_position_volume (stream, position, pa_sw_volume_from_dB (volume_db)); +} + +static gdouble +stream_get_balance (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0); + + return PULSE_STREAM (stream)->priv->balance; +} + +static gboolean +stream_set_balance (MateMixerStream *stream, gdouble balance) +{ + PulseStream *pstream; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + pstream = PULSE_STREAM (stream); + cvolume = pstream->priv->volume; + + if (balance == pstream->priv->balance) + return TRUE; + + if (pa_cvolume_set_balance (&cvolume, + &pstream->priv->channel_map, + (float) balance) == NULL) { + return FALSE; + } + return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gdouble +stream_get_fade (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->fade; +} + +static gboolean +stream_set_fade (MateMixerStream *stream, gdouble fade) +{ + pa_cvolume cvolume; + PulseStream *pstream; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + pstream = PULSE_STREAM (stream); + cvolume = pstream->priv->volume; + + if (fade == pstream->priv->fade) + return TRUE; + + if (pa_cvolume_set_fade (&cvolume, + &pstream->priv->channel_map, + (float) fade) == NULL) { + return FALSE; + } + return PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, &cvolume); +} + +static gboolean +stream_suspend (MateMixerStream *stream) +{ + // TODO + return TRUE; +} + +static gboolean +stream_resume (MateMixerStream *stream) +{ + // TODO + return TRUE; +} + +static const GList * +stream_list_ports (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return (const GList *) PULSE_STREAM (stream)->priv->ports; +} + +static MateMixerPort * +stream_get_active_port (MateMixerStream *stream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + + return PULSE_STREAM (stream)->priv->port; +} + +static gboolean +stream_set_active_port (MateMixerStream *stream, const gchar *port_name) +{ + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (port_name != NULL, FALSE); + + return PULSE_STREAM_GET_CLASS (stream)->set_active_port (stream, port_name); +} + +static gint64 +stream_get_min_volume (MateMixerStream *stream) +{ + return (gint64) PA_VOLUME_MUTED; +} + +static gint64 +stream_get_max_volume (MateMixerStream *stream) +{ + return (gint64) PA_VOLUME_UI_MAX; +} + +static gint64 +stream_get_normal_volume (MateMixerStream *stream) +{ + return (gint64) PA_VOLUME_NORM; +} |