diff options
Diffstat (limited to 'backends/pulse/pulse-stream.c')
-rw-r--r-- | backends/pulse/pulse-stream.c | 1285 |
1 files changed, 717 insertions, 568 deletions
diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c index c7ac52a..fb738ad 100644 --- a/backends/pulse/pulse-stream.c +++ b/backends/pulse/pulse-stream.c @@ -15,12 +15,6 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -// XXX -// - make sure all functions work correctly with flags -// - make sure all functions notify -// - figure out whether functions should g_warning on errors -// - distinguish MateMixer and Pulse variable names - #include <string.h> #include <glib.h> #include <glib-object.h> @@ -40,19 +34,20 @@ struct _PulseStreamPrivate { guint32 index; - guint32 index_device; gchar *name; gchar *description; MateMixerDevice *device; MateMixerStreamFlags flags; MateMixerStreamState state; gboolean mute; - pa_cvolume volume; + guint volume; + pa_cvolume cvolume; pa_volume_t base_volume; pa_channel_map channel_map; gfloat balance; gfloat fade; - GList *ports; + GHashTable *ports; + GList *ports_list; MateMixerPort *port; PulseConnection *connection; PulseMonitor *monitor; @@ -67,142 +62,197 @@ enum { PROP_FLAGS, PROP_STATE, PROP_MUTE, - PROP_NUM_CHANNELS, PROP_VOLUME, PROP_BALANCE, PROP_FADE, - PROP_PORTS, PROP_ACTIVE_PORT, PROP_INDEX, - PROP_CONNECTION, - N_PROPERTIES + PROP_CONNECTION }; -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); +static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); + +static void pulse_stream_class_init (PulseStreamClass *klass); + +static void pulse_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void pulse_stream_init (PulseStream *pstream); +static void pulse_stream_dispose (GObject *object); +static void pulse_stream_finalize (GObject *object); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM, mate_mixer_stream_interface_init)) -/* Interface implementation */ -static const gchar * stream_get_name (MateMixerStream *stream); -static const gchar * stream_get_description (MateMixerStream *stream); -static MateMixerDevice * stream_get_device (MateMixerStream *stream); -static MateMixerStreamFlags stream_get_flags (MateMixerStream *stream); -static MateMixerStreamState stream_get_state (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 guint stream_get_volume (MateMixerStream *stream); -static gboolean stream_set_volume (MateMixerStream *stream, - guint volume); -static gdouble stream_get_decibel (MateMixerStream *stream); -static gboolean stream_set_decibel (MateMixerStream *stream, - gdouble decibel); -static MateMixerChannelPosition stream_get_channel_position (MateMixerStream *stream, - guint channel); -static guint stream_get_channel_volume (MateMixerStream *stream, - guint channel); -static gboolean stream_set_channel_volume (MateMixerStream *stream, - guint channel, - guint volume); -static gdouble stream_get_channel_decibel (MateMixerStream *stream, - guint channel); -static gboolean stream_set_channel_decibel (MateMixerStream *stream, - guint channel, - gdouble decibel); -static gboolean stream_has_position (MateMixerStream *stream, - MateMixerChannelPosition position); -static guint stream_get_position_volume (MateMixerStream *stream, - MateMixerChannelPosition position); -static gboolean stream_set_position_volume (MateMixerStream *stream, - MateMixerChannelPosition position, - guint volume); -static gdouble stream_get_position_decibel (MateMixerStream *stream, - MateMixerChannelPosition position); -static gboolean stream_set_position_decibel (MateMixerStream *stream, - MateMixerChannelPosition position, - gdouble decibel); -static gfloat stream_get_balance (MateMixerStream *stream); -static gboolean stream_set_balance (MateMixerStream *stream, - gfloat balance); -static gfloat stream_get_fade (MateMixerStream *stream); -static gboolean stream_set_fade (MateMixerStream *stream, - gfloat fade); -static gboolean stream_suspend (MateMixerStream *stream); -static gboolean stream_resume (MateMixerStream *stream); - -static gboolean stream_monitor_start (MateMixerStream *stream); -static void stream_monitor_stop (MateMixerStream *stream); -static gboolean stream_monitor_is_running (MateMixerStream *stream); -static gboolean stream_monitor_set_name (MateMixerStream *stream, - const gchar *name); -static void stream_monitor_value (PulseMonitor *monitor, - gdouble value, - 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_name); - -static guint stream_get_min_volume (MateMixerStream *stream); -static guint stream_get_max_volume (MateMixerStream *stream); -static guint stream_get_normal_volume (MateMixerStream *stream); -static guint stream_get_base_volume (MateMixerStream *stream); - -static gboolean stream_set_cvolume (MateMixerStream *stream, - pa_cvolume *volume); -static gint stream_compare_ports (gconstpointer a, - gconstpointer b); +static const gchar * pulse_stream_get_name (MateMixerStream *stream); +static const gchar * pulse_stream_get_description (MateMixerStream *stream); +static MateMixerDevice * pulse_stream_get_device (MateMixerStream *stream); +static MateMixerStreamFlags pulse_stream_get_flags (MateMixerStream *stream); +static MateMixerStreamState pulse_stream_get_state (MateMixerStream *stream); + +static gboolean pulse_stream_get_mute (MateMixerStream *stream); +static gboolean pulse_stream_set_mute (MateMixerStream *stream, + gboolean mute); + +static guint pulse_stream_get_num_channels (MateMixerStream *stream); + +static guint pulse_stream_get_volume (MateMixerStream *stream); +static gboolean pulse_stream_set_volume (MateMixerStream *stream, + guint volume); + +static gdouble pulse_stream_get_decibel (MateMixerStream *stream); +static gboolean pulse_stream_set_decibel (MateMixerStream *stream, + gdouble decibel); + +static guint pulse_stream_get_channel_volume (MateMixerStream *stream, + guint channel); +static gboolean pulse_stream_set_channel_volume (MateMixerStream *stream, + guint channel, + guint volume); + +static gdouble pulse_stream_get_channel_decibel (MateMixerStream *stream, + guint channel); +static gboolean pulse_stream_set_channel_decibel (MateMixerStream *stream, + guint channel, + gdouble decibel); + +static MateMixerChannelPosition pulse_stream_get_channel_position (MateMixerStream *stream, + guint channel); +static gboolean pulse_stream_has_channel_position (MateMixerStream *stream, + MateMixerChannelPosition position); + +static gfloat pulse_stream_get_balance (MateMixerStream *stream); +static gboolean pulse_stream_set_balance (MateMixerStream *stream, + gfloat balance); + +static gfloat pulse_stream_get_fade (MateMixerStream *stream); +static gboolean pulse_stream_set_fade (MateMixerStream *stream, + gfloat fade); + +static gboolean pulse_stream_suspend (MateMixerStream *stream); +static gboolean pulse_stream_resume (MateMixerStream *stream); + +static gboolean pulse_stream_monitor_start (MateMixerStream *stream); +static void pulse_stream_monitor_stop (MateMixerStream *stream); +static gboolean pulse_stream_monitor_is_running (MateMixerStream *stream); +static gboolean pulse_stream_monitor_set_name (MateMixerStream *stream, + const gchar *name); + +static const GList * pulse_stream_list_ports (MateMixerStream *stream); + +static MateMixerPort * pulse_stream_get_active_port (MateMixerStream *stream); +static gboolean pulse_stream_set_active_port (MateMixerStream *stream, + MateMixerPort *port); + +static guint pulse_stream_get_min_volume (MateMixerStream *stream); +static guint pulse_stream_get_max_volume (MateMixerStream *stream); +static guint pulse_stream_get_normal_volume (MateMixerStream *stream); +static guint pulse_stream_get_base_volume (MateMixerStream *stream); + +static void on_monitor_value (PulseMonitor *monitor, + gdouble value, + PulseStream *pstream); + +static gboolean update_balance_fade (PulseStream *pstream); + +static gboolean set_cvolume (PulseStream *pstream, + pa_cvolume *cvolume); + +static gint compare_ports (gconstpointer a, + gconstpointer b); static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) { - iface->get_name = stream_get_name; - iface->get_description = stream_get_description; - iface->get_device = stream_get_device; - iface->get_flags = stream_get_flags; - iface->get_state = stream_get_state; - 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_decibel = stream_get_decibel; - iface->set_decibel = stream_set_decibel; - 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_decibel = stream_get_channel_decibel; - iface->set_channel_decibel = stream_set_channel_decibel; - 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_decibel = stream_get_position_decibel; - iface->set_position_decibel = stream_set_position_decibel; - 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->monitor_start = stream_monitor_start; - iface->monitor_stop = stream_monitor_stop; - iface->monitor_is_running = stream_monitor_is_running; - iface->monitor_set_name = stream_monitor_set_name; - 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; - iface->get_base_volume = stream_get_base_volume; + iface->get_name = pulse_stream_get_name; + iface->get_description = pulse_stream_get_description; + iface->get_device = pulse_stream_get_device; + iface->get_flags = pulse_stream_get_flags; + iface->get_state = pulse_stream_get_state; + iface->get_mute = pulse_stream_get_mute; + iface->set_mute = pulse_stream_set_mute; + iface->get_num_channels = pulse_stream_get_num_channels; + iface->get_volume = pulse_stream_get_volume; + iface->set_volume = pulse_stream_set_volume; + iface->get_decibel = pulse_stream_get_decibel; + iface->set_decibel = pulse_stream_set_decibel; + iface->get_channel_volume = pulse_stream_get_channel_volume; + iface->set_channel_volume = pulse_stream_set_channel_volume; + iface->get_channel_decibel = pulse_stream_get_channel_decibel; + iface->set_channel_decibel = pulse_stream_set_channel_decibel; + iface->get_channel_position = pulse_stream_get_channel_position; + iface->has_channel_position = pulse_stream_has_channel_position; + iface->get_balance = pulse_stream_get_balance; + iface->set_balance = pulse_stream_set_balance; + iface->get_fade = pulse_stream_get_fade; + iface->set_fade = pulse_stream_set_fade; + iface->suspend = pulse_stream_suspend; + iface->resume = pulse_stream_resume; + iface->monitor_start = pulse_stream_monitor_start; + iface->monitor_stop = pulse_stream_monitor_stop; + iface->monitor_is_running = pulse_stream_monitor_is_running; + iface->monitor_set_name = pulse_stream_monitor_set_name; + iface->list_ports = pulse_stream_list_ports; + iface->get_active_port = pulse_stream_get_active_port; + iface->set_active_port = pulse_stream_set_active_port; + iface->get_min_volume = pulse_stream_get_min_volume; + iface->get_max_volume = pulse_stream_get_max_volume; + iface->get_normal_volume = pulse_stream_get_normal_volume; + iface->get_base_volume = pulse_stream_get_base_volume; +} + +static void +pulse_stream_class_init (PulseStreamClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + 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_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_MUTE, "mute"); + g_object_class_override_property (object_class, PROP_VOLUME, "volume"); + g_object_class_override_property (object_class, PROP_BALANCE, "balance"); + g_object_class_override_property (object_class, PROP_FADE, "fade"); + g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port"); + + g_type_class_add_private (object_class, sizeof (PulseStreamPrivate)); } static void @@ -211,52 +261,46 @@ pulse_stream_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - PulseStream *stream; + PulseStream *pstream; - stream = PULSE_STREAM (object); + pstream = PULSE_STREAM (object); switch (param_id) { case PROP_NAME: - g_value_set_string (value, stream->priv->name); + g_value_set_string (value, pstream->priv->name); break; case PROP_DESCRIPTION: - g_value_set_string (value, stream->priv->description); + g_value_set_string (value, pstream->priv->description); break; case PROP_DEVICE: - g_value_set_object (value, stream->priv->device); + g_value_set_object (value, pstream->priv->device); break; case PROP_FLAGS: - g_value_set_flags (value, stream->priv->flags); + g_value_set_flags (value, pstream->priv->flags); break; case PROP_STATE: - g_value_set_enum (value, stream->priv->state); + g_value_set_enum (value, pstream->priv->state); 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))); + g_value_set_boolean (value, pstream->priv->mute); break; case PROP_VOLUME: - g_value_set_int64 (value, stream_get_volume (MATE_MIXER_STREAM (stream))); + g_value_set_uint (value, pstream->priv->volume); break; case PROP_BALANCE: - g_value_set_float (value, stream->priv->balance); + g_value_set_float (value, pstream->priv->balance); break; case PROP_FADE: - g_value_set_float (value, stream->priv->fade); - break; - case PROP_PORTS: - g_value_set_pointer (value, stream->priv->ports); + g_value_set_float (value, pstream->priv->fade); break; case PROP_ACTIVE_PORT: - g_value_set_object (value, stream->priv->port); + g_value_set_object (value, pstream->priv->port); break; case PROP_INDEX: - g_value_set_uint (value, stream->priv->index); + g_value_set_uint (value, pstream->priv->index); break; case PROP_CONNECTION: - g_value_set_object (value, stream->priv->connection); + g_value_set_object (value, pstream->priv->connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -270,17 +314,17 @@ pulse_stream_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - PulseStream *stream; + PulseStream *pstream; - stream = PULSE_STREAM (object); + pstream = PULSE_STREAM (object); switch (param_id) { case PROP_INDEX: - stream->priv->index = g_value_get_uint (value); + pstream->priv->index = g_value_get_uint (value); break; case PROP_CONNECTION: /* Construct-only object */ - stream->priv->connection = g_value_dup_object (value); + pstream->priv->connection = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -289,78 +333,41 @@ pulse_stream_set_property (GObject *object, } static void -pulse_stream_class_init (PulseStreamClass *klass) +pulse_stream_init (PulseStream *pstream) { - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - 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)); + pstream->priv = G_TYPE_INSTANCE_GET_PRIVATE (pstream, + PULSE_TYPE_STREAM, + PulseStreamPrivate); - 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)); + pstream->priv->ports = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); - 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_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_BALANCE, "balance"); - g_object_class_override_property (object_class, PROP_FADE, "fade"); - g_object_class_override_property (object_class, PROP_PORTS, "ports"); - g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port"); - - g_type_class_add_private (object_class, sizeof (PulseStreamPrivate)); -} + /* Initialize empty volume and channel map structures, they will be used + * if the stream does not support volume */ + pa_cvolume_init (&pstream->priv->cvolume); -static void -pulse_stream_init (PulseStream *stream) -{ - stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, - PULSE_TYPE_STREAM, - PulseStreamPrivate); + pa_channel_map_init (&pstream->priv->channel_map); } static void pulse_stream_dispose (GObject *object) { - PulseStream *stream; + PulseStream *pstream; - stream = PULSE_STREAM (object); + pstream = PULSE_STREAM (object); - if (stream->priv->ports) { - g_list_free_full (stream->priv->ports, g_object_unref); - stream->priv->ports = NULL; + if (pstream->priv->ports_list != NULL) { + g_list_free_full (pstream->priv->ports_list, g_object_unref); + pstream->priv->ports_list = NULL; } + g_hash_table_remove_all (pstream->priv->ports); - g_clear_object (&stream->priv->port); - g_clear_object (&stream->priv->device); - g_clear_object (&stream->priv->monitor); - g_clear_object (&stream->priv->connection); + g_clear_object (&pstream->priv->port); + g_clear_object (&pstream->priv->device); + g_clear_object (&pstream->priv->monitor); + g_clear_object (&pstream->priv->connection); G_OBJECT_CLASS (pulse_stream_parent_class)->dispose (object); } @@ -368,220 +375,261 @@ pulse_stream_dispose (GObject *object) static void pulse_stream_finalize (GObject *object) { - PulseStream *stream; + PulseStream *pstream; - stream = PULSE_STREAM (object); + pstream = PULSE_STREAM (object); - g_free (stream->priv->name); - g_free (stream->priv->description); + g_free (pstream->priv->name); + g_free (pstream->priv->description); + g_free (pstream->priv->monitor_name); + + g_hash_table_destroy (pstream->priv->ports); G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object); } guint32 -pulse_stream_get_index (PulseStream *stream) +pulse_stream_get_index (PulseStream *pstream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), 0); - return stream->priv->index; + return pstream->priv->index; } PulseConnection * -pulse_stream_get_connection (PulseStream *stream) +pulse_stream_get_connection (PulseStream *pstream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - return stream->priv->connection; + return pstream->priv->connection; } PulseMonitor * -pulse_stream_get_monitor (PulseStream *stream) +pulse_stream_get_monitor (PulseStream *pstream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - return stream->priv->monitor; + return pstream->priv->monitor; +} + +const pa_cvolume * +pulse_stream_get_cvolume (PulseStream *pstream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); + + return &pstream->priv->cvolume; +} + +const pa_channel_map * +pulse_stream_get_channel_map (PulseStream *pstream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); + + return &pstream->priv->channel_map; +} + +GHashTable * +pulse_stream_get_ports (PulseStream *pstream) +{ + g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); + + return pstream->priv->ports; } gboolean -pulse_stream_update_name (PulseStream *stream, const gchar *name) +pulse_stream_update_name (PulseStream *pstream, const gchar *name) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); /* Allow the name to be NULL */ - if (g_strcmp0 (name, stream->priv->name)) { - g_free (stream->priv->name); - stream->priv->name = g_strdup (name); + if (g_strcmp0 (name, pstream->priv->name) != 0) { + g_free (pstream->priv->name); + pstream->priv->name = g_strdup (name); - g_object_notify (G_OBJECT (stream), "name"); + g_object_notify (G_OBJECT (pstream), "name"); + return TRUE; } - return TRUE; + return FALSE; } gboolean -pulse_stream_update_description (PulseStream *stream, const gchar *description) +pulse_stream_update_description (PulseStream *pstream, const gchar *description) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), 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); + if (g_strcmp0 (description, pstream->priv->description) != 0) { + g_free (pstream->priv->description); + pstream->priv->description = g_strdup (description); - g_object_notify (G_OBJECT (stream), "description"); + g_object_notify (G_OBJECT (pstream), "description"); + return TRUE; } - return TRUE; + return FALSE; } gboolean -pulse_stream_update_device (PulseStream *stream, MateMixerDevice *device) +pulse_stream_update_device (PulseStream *pstream, MateMixerDevice *device) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - if (stream->priv->device != device) { - g_clear_object (&stream->priv->device); + if (pstream->priv->device != device) { + g_clear_object (&pstream->priv->device); if (G_LIKELY (device != NULL)) - stream->priv->device = g_object_ref (device); + pstream->priv->device = g_object_ref (device); - g_object_notify (G_OBJECT (stream), "device"); + g_object_notify (G_OBJECT (pstream), "device"); + return TRUE; } - return TRUE; + return FALSE; } gboolean -pulse_stream_update_flags (PulseStream *stream, MateMixerStreamFlags flags) +pulse_stream_update_flags (PulseStream *pstream, MateMixerStreamFlags flags) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); + + if (pstream->priv->flags != flags) { + pstream->priv->flags = flags; - if (stream->priv->flags != flags) { - stream->priv->flags = flags; - g_object_notify (G_OBJECT (stream), "flags"); + g_object_notify (G_OBJECT (pstream), "flags"); + return TRUE; } - return TRUE; + return FALSE; } gboolean -pulse_stream_update_state (PulseStream *stream, MateMixerStreamState state) +pulse_stream_update_state (PulseStream *pstream, MateMixerStreamState state) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - if (stream->priv->state != state) { - stream->priv->state = state; - g_object_notify (G_OBJECT (stream), "state"); + if (pstream->priv->state != state) { + pstream->priv->state = state; + + g_object_notify (G_OBJECT (pstream), "state"); + return TRUE; } - return TRUE; + return FALSE; } gboolean -pulse_stream_update_mute (PulseStream *stream, gboolean mute) +pulse_stream_update_channel_map (PulseStream *pstream, const pa_channel_map *map) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + MateMixerStreamFlags flags; - if (stream->priv->mute != mute) { - stream->priv->mute = mute; - g_object_notify (G_OBJECT (stream), "mute"); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); + g_return_val_if_fail (map != NULL, FALSE); + + flags = pstream->priv->flags; + + if (pa_channel_map_valid (map)) { + if (pa_channel_map_can_balance (map)) + flags |= MATE_MIXER_STREAM_CAN_BALANCE; + else + flags &= ~MATE_MIXER_STREAM_CAN_BALANCE; + + if (pa_channel_map_can_fade (map)) + flags |= MATE_MIXER_STREAM_CAN_FADE; + else + flags &= ~MATE_MIXER_STREAM_CAN_FADE; + + pstream->priv->channel_map = *map; + } else { + flags &= ~(MATE_MIXER_STREAM_CAN_BALANCE | MATE_MIXER_STREAM_CAN_FADE); + + /* If the channel map is not valid, create an empty channel map, which + * also won't validate, but at least we know what it is */ + pa_channel_map_init (&pstream->priv->channel_map); } + + pulse_stream_update_flags (pstream, flags); return TRUE; } gboolean -pulse_stream_update_volume (PulseStream *stream, - const pa_cvolume *volume, - const pa_channel_map *map, - pa_volume_t base_volume) +pulse_stream_update_volume (PulseStream *pstream, + const pa_cvolume *cvolume, + pa_volume_t base_volume) { - gfloat fade = 0.0f; - gfloat balance = 0.0f; + MateMixerStreamFlags flags; - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - /* The channel map should be always present, but volume is not always - * supported and might be NULL */ - if (G_LIKELY (map != NULL)) { - if (!pa_channel_map_equal (&stream->priv->channel_map, map)) - stream->priv->channel_map = *map; - } + /* The base volume is not a property */ + pstream->priv->base_volume = base_volume; - if (volume != NULL) { - if (!pa_cvolume_equal (&stream->priv->volume, volume)) { - stream->priv->volume = *volume; + flags = pstream->priv->flags; - g_object_notify (G_OBJECT (stream), "volume"); - } + if (cvolume != NULL && pa_cvolume_valid (cvolume)) { + /* Decibel volume and volume settability flags must be provided by + * the implementation */ + flags |= MATE_MIXER_STREAM_HAS_VOLUME; - stream->priv->base_volume = (base_volume > 0) - ? base_volume - : PA_VOLUME_NORM; + if (pa_cvolume_equal (&pstream->priv->cvolume, cvolume) == 0) { + pstream->priv->cvolume = *cvolume; + pstream->priv->volume = (guint) pa_cvolume_max (&pstream->priv->cvolume); - /* Fade and balance need a valid channel map and volume, otherwise - * compare against the default values */ - fade = pa_cvolume_get_fade (volume, &stream->priv->channel_map); - balance = pa_cvolume_get_balance (volume, &stream->priv->channel_map); + g_object_notify (G_OBJECT (pstream), "volume"); + } } else { - stream->priv->base_volume = PA_VOLUME_NORM; - } + flags &= ~(MATE_MIXER_STREAM_HAS_VOLUME | + MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME | + MATE_MIXER_STREAM_CAN_SET_VOLUME); - if (stream->priv->balance != balance) { - stream->priv->balance = balance; - g_object_notify (G_OBJECT (stream), "balance"); - } + /* If the cvolume is not valid, create an empty cvolume, which also + * won't validate, but at least we know what it is */ + pa_cvolume_init (&pstream->priv->cvolume); + + if (pstream->priv->volume != (guint) PA_VOLUME_MUTED) { + pstream->priv->volume = (guint) PA_VOLUME_MUTED; - if (stream->priv->fade != fade) { - stream->priv->fade = fade; - g_object_notify (G_OBJECT (stream), "fade"); + g_object_notify (G_OBJECT (pstream), "volume"); + } } + + pulse_stream_update_flags (pstream, flags); + + /* Changing volume may change the balance and fade values as well */ + update_balance_fade (pstream); return TRUE; } gboolean -pulse_stream_update_ports (PulseStream *stream, GList *ports) +pulse_stream_update_mute (PulseStream *pstream, gboolean mute) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - if (stream->priv->ports) - g_list_free_full (stream->priv->ports, g_object_unref); + if (pstream->priv->mute != mute) { + pstream->priv->mute = mute; - if (ports) - stream->priv->ports = g_list_sort (ports, stream_compare_ports); - else - stream->priv->ports = NULL; - - g_object_notify (G_OBJECT (stream), "ports"); - return TRUE; + g_object_notify (G_OBJECT (pstream), "mute"); + return TRUE; + } + return FALSE; } gboolean -pulse_stream_update_active_port (PulseStream *stream, const gchar *port_name) +pulse_stream_update_active_port (PulseStream *pstream, MateMixerPort *port) { - GList *list; - MateMixerPort *port = NULL; + g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); + g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - 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 (pstream->priv->port != port) { + if (pstream->priv->port != NULL) + g_clear_object (&pstream->priv->port); - if (stream->priv->port != port) { - if (stream->priv->port) - g_clear_object (&stream->priv->port); - if (port) - stream->priv->port = g_object_ref (port); + if (port != NULL) + pstream->priv->port = g_object_ref (port); - g_object_notify (G_OBJECT (stream), "active-port"); + g_object_notify (G_OBJECT (pstream), "active-port"); + return TRUE; } - return TRUE; + return FALSE; } static const gchar * -stream_get_name (MateMixerStream *stream) +pulse_stream_get_name (MateMixerStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); @@ -589,7 +637,7 @@ stream_get_name (MateMixerStream *stream) } static const gchar * -stream_get_description (MateMixerStream *stream) +pulse_stream_get_description (MateMixerStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); @@ -597,7 +645,7 @@ stream_get_description (MateMixerStream *stream) } static MateMixerDevice * -stream_get_device (MateMixerStream *stream) +pulse_stream_get_device (MateMixerStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); @@ -605,7 +653,7 @@ stream_get_device (MateMixerStream *stream) } static MateMixerStreamFlags -stream_get_flags (MateMixerStream *stream) +pulse_stream_get_flags (MateMixerStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS); @@ -613,269 +661,253 @@ stream_get_flags (MateMixerStream *stream) } static MateMixerStreamState -stream_get_state (MateMixerStream *stream) +pulse_stream_get_state (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_UNKNOWN_STATE); + g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN); return PULSE_STREAM (stream)->priv->state; } static gboolean -stream_get_mute (MateMixerStream *stream) +pulse_stream_get_mute (MateMixerStream *stream) { + PulseStream *pstream; + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - return PULSE_STREAM (stream)->priv->mute; + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE)) + return FALSE; + + return pstream->priv->mute; } static gboolean -stream_set_mute (MateMixerStream *stream, gboolean mute) +pulse_stream_set_mute (MateMixerStream *stream, gboolean mute) { - PulseStream *pulse; + PulseStream *pstream; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pulse = PULSE_STREAM (stream); + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE)) + return FALSE; - if (pulse->priv->mute != mute) { - if (PULSE_STREAM_GET_CLASS (stream)->set_mute (stream, mute) == FALSE) + if (pstream->priv->mute != mute) { + PulseStreamClass *klass = PULSE_STREAM_GET_CLASS (pstream); + + if (klass->set_mute (pstream, mute) == FALSE) return FALSE; - pulse->priv->mute = mute; + pstream->priv->mute = mute; + g_object_notify (G_OBJECT (stream), "mute"); } return TRUE; } static guint -stream_get_num_channels (MateMixerStream *stream) +pulse_stream_get_num_channels (MateMixerStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); - return PULSE_STREAM (stream)->priv->volume.channels; + return PULSE_STREAM (stream)->priv->channel_map.channels; } static guint -stream_get_volume (MateMixerStream *stream) +pulse_stream_get_volume (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + PulseStream *pstream; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); + + pstream = PULSE_STREAM (stream); - return (guint) pa_cvolume_max (&PULSE_STREAM (stream)->priv->volume); + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) + return (guint) PA_VOLUME_MUTED; + + return pstream->priv->volume; } static gboolean -stream_set_volume (MateMixerStream *stream, guint volume) +pulse_stream_set_volume (MateMixerStream *stream, guint volume) { - PulseStream *pulse; - pa_cvolume cvolume; + PulseStream *pstream; + pa_cvolume cvolume; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME)) + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) return FALSE; - pulse = PULSE_STREAM (stream); - cvolume = pulse->priv->volume; + cvolume = pstream->priv->cvolume; if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) return FALSE; - return stream_set_cvolume (stream, &cvolume); + return set_cvolume (pstream, &cvolume); } static gdouble -stream_get_decibel (MateMixerStream *stream) +pulse_stream_get_decibel (MateMixerStream *stream) { - gdouble value; + PulseStream *pstream; + gdouble value; g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY); - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) return -MATE_MIXER_INFINITY; - value = pa_sw_volume_to_dB (stream_get_volume (stream)); + value = pa_sw_volume_to_dB (pulse_stream_get_volume (stream)); + /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */ return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; } static gboolean -stream_set_decibel (MateMixerStream *stream, gdouble decibel) +pulse_stream_set_decibel (MateMixerStream *stream, gdouble decibel) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) || - !(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME)) - return FALSE; - - return stream_set_volume (stream, pa_sw_volume_from_dB (decibel)); -} + PulseStream *pstream; -static MateMixerChannelPosition -stream_get_channel_position (MateMixerStream *stream, guint channel) -{ - PulseStream *pulse; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN_POSITION); + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pulse = PULSE_STREAM (stream); + pstream = PULSE_STREAM (stream); - if (channel >= pulse->priv->channel_map.channels) - return MATE_MIXER_CHANNEL_UNKNOWN_POSITION; + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) || + !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) + return FALSE; - return pulse_convert_position_to_pulse (pulse->priv->channel_map.map[channel]); + return pulse_stream_set_volume (stream, pa_sw_volume_from_dB (decibel)); } static guint -stream_get_channel_volume (MateMixerStream *stream, guint channel) +pulse_stream_get_channel_volume (MateMixerStream *stream, guint channel) { - PulseStream *pulse; + PulseStream *pstream; - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); + + pstream = PULSE_STREAM (stream); - pulse = PULSE_STREAM (stream); + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) + return (guint) PA_VOLUME_MUTED; - if (channel >= pulse->priv->volume.channels) - return stream_get_min_volume (stream); + if (channel >= pstream->priv->cvolume.channels) + return (guint) PA_VOLUME_MUTED; - return (guint) pulse->priv->volume.values[channel]; + return (guint) pstream->priv->cvolume.values[channel]; } static gboolean -stream_set_channel_volume (MateMixerStream *stream, guint channel, guint volume) +pulse_stream_set_channel_volume (MateMixerStream *stream, guint channel, guint volume) { - PulseStream *pulse; - pa_cvolume cvolume; + PulseStream *pstream; + pa_cvolume cvolume; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pulse = PULSE_STREAM (stream); - cvolume = pulse->priv->volume; + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) + return FALSE; - if (channel >= pulse->priv->volume.channels) + if (channel >= pstream->priv->cvolume.channels) return FALSE; + /* This is safe, because the cvolume is validated by set_cvolume() */ + cvolume = pstream->priv->cvolume; cvolume.values[channel] = (pa_volume_t) volume; - return stream_set_cvolume (stream, &cvolume); + return set_cvolume (pstream, &cvolume); } static gdouble -stream_get_channel_decibel (MateMixerStream *stream, guint channel) +pulse_stream_get_channel_decibel (MateMixerStream *stream, guint channel) { - PulseStream *pulse; + PulseStream *pstream; gdouble value; g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY); - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) - return -MATE_MIXER_INFINITY; + pstream = PULSE_STREAM (stream); - pulse = PULSE_STREAM (stream); + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) + return -MATE_MIXER_INFINITY; - if (channel >= pulse->priv->volume.channels) + if (channel >= pstream->priv->cvolume.channels) return -MATE_MIXER_INFINITY; - value = pa_sw_volume_to_dB (pulse->priv->volume.values[channel]); + value = pa_sw_volume_to_dB (pstream->priv->cvolume.values[channel]); return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; } static gboolean -stream_set_channel_decibel (MateMixerStream *stream, - guint channel, - gdouble decibel) +pulse_stream_set_channel_decibel (MateMixerStream *stream, + guint channel, + gdouble decibel) { + PulseStream *pstream; + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) || - !(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME)) + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) || + !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) return FALSE; - return stream_set_channel_volume (stream, channel, pa_sw_volume_from_dB (decibel)); + return pulse_stream_set_channel_volume (stream, channel, pa_sw_volume_from_dB (decibel)); } -static gboolean -stream_has_position (MateMixerStream *stream, MateMixerChannelPosition position) +static MateMixerChannelPosition +pulse_stream_get_channel_position (MateMixerStream *stream, guint channel) { - PulseStream *pulse; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pulse = PULSE_STREAM (stream); + PulseStream *pstream; - return pa_channel_map_has_position (&pulse->priv->channel_map, - pulse_convert_position_to_pulse (position)); -} - -static guint -stream_get_position_volume (MateMixerStream *stream, - MateMixerChannelPosition position) -{ - PulseStream *pulse; + g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN); - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + pstream = PULSE_STREAM (stream); - pulse = PULSE_STREAM (stream); + if (channel >= pstream->priv->channel_map.channels) + return MATE_MIXER_CHANNEL_UNKNOWN; - return pa_cvolume_get_position (&pulse->priv->volume, - &pulse->priv->channel_map, - pulse_convert_position_to_pulse (position)); + return pulse_convert_position_to_pulse (pstream->priv->channel_map.map[channel]); } static gboolean -stream_set_position_volume (MateMixerStream *stream, - MateMixerChannelPosition position, - guint volume) +pulse_stream_has_channel_position (MateMixerStream *stream, + MateMixerChannelPosition position) { - PulseStream *pulse; - pa_cvolume cvolume; + PulseStream *pstream; + pa_channel_position_t p; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pulse = PULSE_STREAM (stream); - cvolume = pulse->priv->volume; - - if (!pa_cvolume_set_position (&cvolume, - &pulse->priv->channel_map, - pulse_convert_position_to_pulse (position), - (pa_volume_t) volume)) - return FALSE; - - return stream_set_cvolume (stream, &cvolume); -} - -static gdouble -stream_get_position_decibel (MateMixerStream *stream, - MateMixerChannelPosition position) -{ - gdouble value; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY); - - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) - return -MATE_MIXER_INFINITY; - - value = pa_sw_volume_to_dB (stream_get_position_volume (stream, position)); + pstream = PULSE_STREAM (stream); - return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; -} - -static gboolean -stream_set_position_decibel (MateMixerStream *stream, - MateMixerChannelPosition position, - gdouble decibel) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); + /* Handle invalid position as a special case, otherwise this function would + * return TRUE for e.g. unknown index in a default channel map */ + p = pulse_convert_position_to_pulse (position); - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) || - !(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CAN_SET_VOLUME)) + if (p == PA_CHANNEL_POSITION_INVALID) return FALSE; - return stream_set_position_volume (stream, position, pa_sw_volume_from_dB (decibel)); + if (pa_channel_map_has_position (&pstream->priv->channel_map, p) != 0) + return TRUE; + else + return FALSE; } static gfloat -stream_get_balance (MateMixerStream *stream) +pulse_stream_get_balance (MateMixerStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f); @@ -883,24 +915,28 @@ stream_get_balance (MateMixerStream *stream) } static gboolean -stream_set_balance (MateMixerStream *stream, gfloat balance) +pulse_stream_set_balance (MateMixerStream *stream, gfloat balance) { - PulseStream *pulse; + PulseStream *pstream; pa_cvolume cvolume; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pulse = PULSE_STREAM (stream); - cvolume = pulse->priv->volume; + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_BALANCE)) + return FALSE; + + cvolume = pstream->priv->cvolume; - if (pa_cvolume_set_balance (&cvolume, &pulse->priv->channel_map, balance) == NULL) + if (pa_cvolume_set_balance (&cvolume, &pstream->priv->channel_map, balance) == NULL) return FALSE; - return stream_set_cvolume (stream, &cvolume); + return set_cvolume (pstream, &cvolume); } static gfloat -stream_get_fade (MateMixerStream *stream) +pulse_stream_get_fade (MateMixerStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f); @@ -908,138 +944,178 @@ stream_get_fade (MateMixerStream *stream) } static gboolean -stream_set_fade (MateMixerStream *stream, gfloat fade) +pulse_stream_set_fade (MateMixerStream *stream, gfloat fade) { - PulseStream *pulse; + PulseStream *pstream; pa_cvolume cvolume; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pulse = PULSE_STREAM (stream); - cvolume = pulse->priv->volume; + pstream = PULSE_STREAM (stream); - if (pa_cvolume_set_fade (&cvolume, &pulse->priv->channel_map, fade) == NULL) + if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_FADE)) return FALSE; - return stream_set_cvolume (stream, &cvolume); + cvolume = pstream->priv->cvolume; + + if (pa_cvolume_set_fade (&cvolume, &pstream->priv->channel_map, fade) == NULL) + return FALSE; + + return set_cvolume (pstream, &cvolume); } static gboolean -stream_suspend (MateMixerStream *stream) +pulse_stream_suspend (MateMixerStream *stream) { + PulseStream *pstream; + PulseStreamClass *klass; + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - if (!(PULSE_STREAM (stream)->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) return FALSE; - return PULSE_STREAM_GET_CLASS (stream)->suspend (stream); + if (pstream->priv->state == MATE_MIXER_STREAM_STATE_SUSPENDED) + return FALSE; + + klass = PULSE_STREAM_GET_CLASS (pstream); + + if (klass->suspend (pstream) == FALSE) + return FALSE; + + pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED); + return TRUE; } static gboolean -stream_resume (MateMixerStream *stream) +pulse_stream_resume (MateMixerStream *stream) { + PulseStream *pstream; + PulseStreamClass *klass; + g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - if (!(PULSE_STREAM (stream)->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) + return FALSE; + + if (pstream->priv->state != MATE_MIXER_STREAM_STATE_SUSPENDED) return FALSE; - return PULSE_STREAM_GET_CLASS (stream)->resume (stream); + klass = PULSE_STREAM_GET_CLASS (pstream); + + if (klass->resume (pstream) == FALSE) + return FALSE; + + /* The state when resumed should be either RUNNING or IDLE, let's assume + * IDLE for now and request an immediate update */ + pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE); + + klass->reload (pstream); + return TRUE; } static gboolean -stream_monitor_start (MateMixerStream *stream) +pulse_stream_monitor_start (MateMixerStream *stream) { - PulseStream *pulse; + PulseStream *pstream; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pulse = PULSE_STREAM (stream); + pstream = PULSE_STREAM (stream); - if (!pulse->priv->monitor) { - pulse->priv->monitor = PULSE_STREAM_GET_CLASS (stream)->create_monitor (stream); + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MONITOR)) + return FALSE; + + if (pstream->priv->monitor == NULL) { + pstream->priv->monitor = PULSE_STREAM_GET_CLASS (pstream)->create_monitor (pstream); - if (G_UNLIKELY (pulse->priv->monitor == NULL)) + if (G_UNLIKELY (pstream->priv->monitor == NULL)) return FALSE; - pulse_monitor_set_name (pulse->priv->monitor, - pulse->priv->monitor_name); + pulse_monitor_set_name (pstream->priv->monitor, + pstream->priv->monitor_name); - g_signal_connect (G_OBJECT (pulse->priv->monitor), + g_signal_connect (G_OBJECT (pstream->priv->monitor), "value", - G_CALLBACK (stream_monitor_value), - stream); + G_CALLBACK (on_monitor_value), + pstream); } - g_debug ("Enabling monitor for stream %s", pulse->priv->name); - return pulse_monitor_enable (pulse->priv->monitor); + return pulse_monitor_set_enabled (pstream->priv->monitor, TRUE); } static void -stream_monitor_stop (MateMixerStream *stream) +pulse_stream_monitor_stop (MateMixerStream *stream) { - PulseStream *pulse; + PulseStream *pstream; g_return_if_fail (PULSE_IS_STREAM (stream)); - pulse = PULSE_STREAM (stream); - - if (pulse->priv->monitor && - pulse_monitor_is_enabled (pulse->priv->monitor)) { - g_debug ("Disabling monitor for stream %s", pulse->priv->name); + pstream = PULSE_STREAM (stream); - pulse_monitor_disable (pulse->priv->monitor); - } + if (pstream->priv->monitor != NULL) + pulse_monitor_set_enabled (pstream->priv->monitor, FALSE); } static gboolean -stream_monitor_is_running (MateMixerStream *stream) +pulse_stream_monitor_is_running (MateMixerStream *stream) { - PulseStream *pulse; + PulseStream *pstream; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pulse = PULSE_STREAM (stream); + pstream = PULSE_STREAM (stream); - if (pulse->priv->monitor) - return pulse_monitor_is_enabled (pulse->priv->monitor); + if (pstream->priv->monitor != NULL) + return pulse_monitor_get_enabled (pstream->priv->monitor); return FALSE; } static gboolean -stream_monitor_set_name (MateMixerStream *stream, const gchar *name) +pulse_stream_monitor_set_name (MateMixerStream *stream, const gchar *name) { - PulseStream *pulse; + PulseStream *pstream; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - pulse = PULSE_STREAM (stream); + pstream = PULSE_STREAM (stream); - if (pulse->priv->monitor) - pulse_monitor_set_name (pulse->priv->monitor, name); + if (pstream->priv->monitor != NULL) + pulse_monitor_set_name (pstream->priv->monitor, name); - pulse->priv->monitor_name = g_strdup (name); + pstream->priv->monitor_name = g_strdup (name); return TRUE; } -static void -stream_monitor_value (PulseMonitor *monitor, gdouble value, MateMixerStream *stream) -{ - g_signal_emit_by_name (G_OBJECT (stream), - "monitor-value", - value); -} - static const GList * -stream_list_ports (MateMixerStream *stream) +pulse_stream_list_ports (MateMixerStream *stream) { + PulseStream *pstream; + g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - return (const GList *) PULSE_STREAM (stream)->priv->ports; + pstream = PULSE_STREAM (stream); + + if (pstream->priv->ports_list == NULL) { + GList *list = g_hash_table_get_values (pstream->priv->ports); + + if (list != NULL) { + g_list_foreach (list, (GFunc) g_object_ref, NULL); + + pstream->priv->ports_list = g_list_sort (list, compare_ports); + } + } + + return (const GList *) pstream->priv->ports_list; } static MateMixerPort * -stream_get_active_port (MateMixerStream *stream) +pulse_stream_get_active_port (MateMixerStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); @@ -1047,90 +1123,163 @@ stream_get_active_port (MateMixerStream *stream) } static gboolean -stream_set_active_port (MateMixerStream *stream, const gchar *port_name) +pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) { - PulseStream *pulse; - GList *list; - MateMixerPort *port = NULL; + PulseStream *pstream; + PulseStreamClass *klass; + const gchar *name; g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - g_return_val_if_fail (port_name != NULL, FALSE); + g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - pulse = PULSE_STREAM (stream); - list = pulse->priv->ports; - while (list) { - port = MATE_MIXER_PORT (list->data); + pstream = PULSE_STREAM (stream); - if (!g_strcmp0 (mate_mixer_port_get_name (port), port_name)) - break; + /* Make sure the port comes from this stream */ + name = mate_mixer_port_get_name (port); - port = NULL; - list = list->next; + if (g_hash_table_lookup (pstream->priv->ports, name) == NULL) { + g_warning ("Port %s does not belong to stream %s", + mate_mixer_port_get_name (port), + mate_mixer_stream_get_name (stream)); + return FALSE; } - if (port == NULL || - PULSE_STREAM_GET_CLASS (stream)->set_active_port (stream, port_name) == FALSE) + klass = PULSE_STREAM_GET_CLASS (pstream); + + /* Change the port */ + if (klass->set_active_port (pstream, port) == FALSE) return FALSE; - if (pulse->priv->port) - g_object_unref (pulse->priv->port); + if (pstream->priv->port != NULL) + g_object_unref (pstream->priv->port); - pulse->priv->port = g_object_ref (port); + pstream->priv->port = g_object_ref (port); g_object_notify (G_OBJECT (stream), "active-port"); return TRUE; } static guint -stream_get_min_volume (MateMixerStream *stream) +pulse_stream_get_min_volume (MateMixerStream *stream) { return (guint) PA_VOLUME_MUTED; } static guint -stream_get_max_volume (MateMixerStream *stream) +pulse_stream_get_max_volume (MateMixerStream *stream) { + PulseStream *pstream; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); + + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) + return (guint) PA_VOLUME_MUTED; + return (guint) PA_VOLUME_UI_MAX; } static guint -stream_get_normal_volume (MateMixerStream *stream) +pulse_stream_get_normal_volume (MateMixerStream *stream) { + PulseStream *pstream; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); + + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) + return (guint) PA_VOLUME_MUTED; + return (guint) PA_VOLUME_NORM; } static guint -stream_get_base_volume (MateMixerStream *stream) +pulse_stream_get_base_volume (MateMixerStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); + PulseStream *pstream; + + g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); + + pstream = PULSE_STREAM (stream); + + if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) + return (guint) PA_VOLUME_MUTED; + + if (pstream->priv->base_volume > 0) + return pstream->priv->base_volume; + else + return (guint) PA_VOLUME_NORM; +} + +static void +on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStream *pstream) +{ + g_signal_emit_by_name (G_OBJECT (pstream), + "monitor-value", + value); +} + +static gboolean +update_balance_fade (PulseStream *pstream) +{ + gfloat fade; + gfloat balance; + gboolean changed = FALSE; + + /* The PulseAudio return the default 0.0f values on errors, so skip checking + * validity of the channel map and volume */ + balance = pa_cvolume_get_balance (&pstream->priv->cvolume, + &pstream->priv->channel_map); + + if (pstream->priv->balance != balance) { + pstream->priv->balance = balance; - return PULSE_STREAM (stream)->priv->base_volume; + g_object_notify (G_OBJECT (pstream), "balance"); + changed = TRUE; + } + + fade = pa_cvolume_get_fade (&pstream->priv->cvolume, + &pstream->priv->channel_map); + + if (pstream->priv->fade != fade) { + pstream->priv->fade = fade; + + g_object_notify (G_OBJECT (pstream), "fade"); + changed = TRUE; + } + + return changed; } static gboolean -stream_set_cvolume (MateMixerStream *stream, pa_cvolume *volume) +set_cvolume (PulseStream *pstream, pa_cvolume *cvolume) { - PulseStream *pulse; + PulseStreamClass *klass; - if (!pa_cvolume_valid (volume)) + if (pa_cvolume_valid (cvolume) == 0) return FALSE; + if (pa_cvolume_equal (cvolume, &pstream->priv->cvolume) != 0) + return TRUE; - pulse = PULSE_STREAM (stream); + klass = PULSE_STREAM_GET_CLASS (pstream); - if (!pa_cvolume_equal (volume, &pulse->priv->volume)) { - if (PULSE_STREAM_GET_CLASS (stream)->set_volume (stream, volume) == FALSE) - return FALSE; + if (klass->set_volume (pstream, cvolume) == FALSE) + return FALSE; - pulse->priv->volume = *volume; - g_object_notify (G_OBJECT (stream), "volume"); + pstream->priv->cvolume = *cvolume; + pstream->priv->volume = (guint) pa_cvolume_max (cvolume); - // XXX notify fade and balance - } + g_object_notify (G_OBJECT (pstream), "volume"); + + /* Changing volume may change the balance and fade values as well */ + update_balance_fade (pstream); return TRUE; } static gint -stream_compare_ports (gconstpointer a, gconstpointer b) +compare_ports (gconstpointer a, gconstpointer b) { MateMixerPort *p1 = MATE_MIXER_PORT (a); MateMixerPort *p2 = MATE_MIXER_PORT (b); |