summaryrefslogtreecommitdiff
path: root/backends/pulse/pulse-stream-control.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/pulse/pulse-stream-control.c')
-rw-r--r--backends/pulse/pulse-stream-control.c744
1 files changed, 744 insertions, 0 deletions
diff --git a/backends/pulse/pulse-stream-control.c b/backends/pulse/pulse-stream-control.c
new file mode 100644
index 0000000..fa17e6b
--- /dev/null
+++ b/backends/pulse/pulse-stream-control.c
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+#include "pulse-helpers.h"
+#include "pulse-monitor.h"
+#include "pulse-stream-control.h"
+
+struct _PulseStreamControlPrivate
+{
+ guint32 index;
+ guint volume;
+ pa_cvolume cvolume;
+ pa_volume_t base_volume;
+ pa_channel_map channel_map;
+ PulseConnection *connection;
+ PulseMonitor *monitor;
+ MateMixerAppInfo *app_info;
+};
+
+enum {
+ PROP_0,
+ PROP_INDEX,
+ PROP_CONNECTION,
+ N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+static void pulse_stream_control_class_init (PulseStreamControlClass *klass);
+
+static void pulse_stream_control_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_stream_control_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void pulse_stream_control_init (PulseStreamControl *control);
+static void pulse_stream_control_dispose (GObject *object);
+static void pulse_stream_control_finalize (GObject *object);
+
+G_DEFINE_ABSTRACT_TYPE (PulseStreamControl, pulse_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL)
+
+static MateMixerAppInfo * pulse_stream_control_get_app_info (MateMixerStreamControl *mmsc);
+
+static gboolean pulse_stream_control_set_mute (MateMixerStreamControl *mmsc,
+ gboolean mute);
+
+static guint pulse_stream_control_get_num_channels (MateMixerStreamControl *mmsc);
+
+static guint pulse_stream_control_get_volume (MateMixerStreamControl *mmsc);
+static gboolean pulse_stream_control_set_volume (MateMixerStreamControl *mmsc,
+ guint volume);
+
+static gdouble pulse_stream_control_get_decibel (MateMixerStreamControl *mmsc);
+static gboolean pulse_stream_control_set_decibel (MateMixerStreamControl *mmsc,
+ gdouble decibel);
+
+static guint pulse_stream_control_get_channel_volume (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean pulse_stream_control_set_channel_volume (MateMixerStreamControl *mmsc,
+ guint channel,
+ guint volume);
+
+static gdouble pulse_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean pulse_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc,
+ guint channel,
+ gdouble decibel);
+
+static MateMixerChannelPosition pulse_stream_control_get_channel_position (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean pulse_stream_control_has_channel_position (MateMixerStreamControl *mmsc,
+ MateMixerChannelPosition position);
+
+static gboolean pulse_stream_control_set_balance (MateMixerStreamControl *mmsc,
+ gfloat balance);
+
+static gboolean pulse_stream_control_set_fade (MateMixerStreamControl *mmsc,
+ gfloat fade);
+
+static gboolean pulse_stream_control_get_monitor_enabled (MateMixerStreamControl *mmsc);
+static gboolean pulse_stream_control_set_monitor_enabled (MateMixerStreamControl *mmsc,
+ gboolean enabled);
+
+static guint pulse_stream_control_get_min_volume (MateMixerStreamControl *mmsc);
+static guint pulse_stream_control_get_max_volume (MateMixerStreamControl *mmsc);
+static guint pulse_stream_control_get_normal_volume (MateMixerStreamControl *mmsc);
+static guint pulse_stream_control_get_base_volume (MateMixerStreamControl *mmsc);
+
+static void on_monitor_value (PulseMonitor *monitor,
+ gdouble value,
+ PulseStreamControl *control);
+
+static void set_balance_fade (PulseStreamControl *control);
+
+static gboolean set_cvolume (PulseStreamControl *control,
+ pa_cvolume *cvolume);
+
+static void
+pulse_stream_control_class_init (PulseStreamControlClass *klass)
+{
+ GObjectClass *object_class;
+ MateMixerStreamControlClass *control_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_stream_control_dispose;
+ object_class->finalize = pulse_stream_control_finalize;
+ object_class->get_property = pulse_stream_control_get_property;
+ object_class->set_property = pulse_stream_control_set_property;
+
+ control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
+ control_class->get_app_info = pulse_stream_control_get_app_info;
+ control_class->set_mute = pulse_stream_control_set_mute;
+ control_class->get_num_channels = pulse_stream_control_get_num_channels;
+ control_class->get_volume = pulse_stream_control_get_volume;
+ control_class->set_volume = pulse_stream_control_set_volume;
+ control_class->get_decibel = pulse_stream_control_get_decibel;
+ control_class->set_decibel = pulse_stream_control_set_decibel;
+ control_class->get_channel_volume = pulse_stream_control_get_channel_volume;
+ control_class->set_channel_volume = pulse_stream_control_set_channel_volume;
+ control_class->get_channel_decibel = pulse_stream_control_get_channel_decibel;
+ control_class->set_channel_decibel = pulse_stream_control_set_channel_decibel;
+ control_class->get_channel_position = pulse_stream_control_get_channel_position;
+ control_class->has_channel_position = pulse_stream_control_has_channel_position;
+ control_class->set_balance = pulse_stream_control_set_balance;
+ control_class->set_fade = pulse_stream_control_set_fade;
+ control_class->get_monitor_enabled = pulse_stream_control_get_monitor_enabled;
+ control_class->set_monitor_enabled = pulse_stream_control_set_monitor_enabled;
+ control_class->get_min_volume = pulse_stream_control_get_min_volume;
+ control_class->get_max_volume = pulse_stream_control_get_max_volume;
+ control_class->get_normal_volume = pulse_stream_control_get_normal_volume;
+ control_class->get_base_volume = pulse_stream_control_get_base_volume;
+
+ properties[PROP_INDEX] =
+ g_param_spec_uint ("index",
+ "Index",
+ "Index of the stream control",
+ 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);
+
+ g_type_class_add_private (object_class, sizeof (PulseStreamControlPrivate));
+}
+
+static void
+pulse_stream_control_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PulseStreamControl *control;
+
+ control = PULSE_STREAM_CONTROL (object);
+
+ switch (param_id) {
+ case PROP_INDEX:
+ g_value_set_uint (value, control->priv->index);
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, control->priv->connection);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_stream_control_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PulseStreamControl *control;
+
+ control = PULSE_STREAM_CONTROL (object);
+
+ switch (param_id) {
+ case PROP_INDEX:
+ control->priv->index = g_value_get_uint (value);
+ break;
+ case PROP_CONNECTION:
+ control->priv->connection = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_stream_control_init (PulseStreamControl *control)
+{
+ control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control,
+ PULSE_TYPE_STREAM_CONTROL,
+ PulseStreamControlPrivate);
+
+ /* Initialize empty volume and channel map structures, they will be used
+ * if the stream does not support volume */
+ pa_cvolume_init (&control->priv->cvolume);
+
+ pa_channel_map_init (&control->priv->channel_map);
+}
+
+static void
+pulse_stream_control_dispose (GObject *object)
+{
+ PulseStreamControl *control;
+
+ control = PULSE_STREAM_CONTROL (object);
+
+ g_clear_object (&control->priv->monitor);
+ g_clear_object (&control->priv->connection);
+
+ G_OBJECT_CLASS (pulse_stream_control_parent_class)->dispose (object);
+}
+
+static void
+pulse_stream_control_finalize (GObject *object)
+{
+ PulseStreamControl *control;
+
+ control = PULSE_STREAM_CONTROL (object);
+
+ if (control->priv->app_info != NULL)
+ _mate_mixer_app_info_free (control->priv->app_info);
+
+ G_OBJECT_CLASS (pulse_stream_control_parent_class)->finalize (object);
+}
+
+guint32
+pulse_stream_control_get_index (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), 0);
+
+ return control->priv->index;
+}
+
+PulseConnection *
+pulse_stream_control_get_connection (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
+
+ return control->priv->connection;
+}
+
+PulseMonitor *
+pulse_stream_control_get_monitor (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
+
+ return control->priv->monitor;
+}
+
+const pa_cvolume *
+pulse_stream_control_get_cvolume (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
+
+ return &control->priv->cvolume;
+}
+
+const pa_channel_map *
+pulse_stream_control_get_channel_map (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
+
+ return &control->priv->channel_map;
+}
+
+void
+pulse_stream_control_set_app_info (PulseStreamControl *control, MateMixerAppInfo *info)
+{
+ g_return_if_fail (PULSE_IS_STREAM_CONTROL (control));
+
+ if G_UNLIKELY (control->priv->app_info)
+ _mate_mixer_app_info_free (control->priv->app_info);
+
+ control->priv->app_info = info;
+}
+
+void
+pulse_stream_control_set_channel_map (PulseStreamControl *control, const pa_channel_map *map)
+{
+ MateMixerStreamControlFlags flags;
+
+ g_return_if_fail (PULSE_IS_STREAM_CONTROL (control));
+ g_return_if_fail (map != NULL);
+
+ flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control));
+
+ if (pa_channel_map_valid (map)) {
+ if (pa_channel_map_can_balance (map))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+ else
+ flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+
+ if (pa_channel_map_can_fade (map))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+ else
+ flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+
+ control->priv->channel_map = *map;
+ } else {
+ flags &= ~(MATE_MIXER_STREAM_CONTROL_CAN_BALANCE | MATE_MIXER_STREAM_CONTROL_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 (&control->priv->channel_map);
+ }
+
+ _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags);
+}
+
+void
+pulse_stream_control_set_cvolume (PulseStreamControl *control,
+ const pa_cvolume *cvolume,
+ pa_volume_t base_volume)
+{
+ MateMixerStreamControlFlags flags;
+
+ g_return_if_fail (PULSE_IS_STREAM_CONTROL (control));
+
+ /* The base volume is not a property */
+ control->priv->base_volume = base_volume;
+
+ flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control));
+
+ g_object_freeze_notify (G_OBJECT (control));
+
+ if (cvolume != NULL && pa_cvolume_valid (cvolume)) {
+ /* Decibel volume and volume settability flags must be provided by
+ * the implementation */
+ flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE;
+
+ if (pa_cvolume_equal (&control->priv->cvolume, cvolume) == 0) {
+ control->priv->cvolume = *cvolume;
+ control->priv->volume = (guint) pa_cvolume_max (&control->priv->cvolume);
+
+ g_object_notify (G_OBJECT (control), "volume");
+ }
+ } else {
+ flags &= ~(MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL);
+
+ /* 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 (&control->priv->cvolume);
+
+ if (control->priv->volume != (guint) PA_VOLUME_MUTED) {
+ control->priv->volume = (guint) PA_VOLUME_MUTED;
+
+ g_object_notify (G_OBJECT (control), "volume");
+ }
+ }
+
+ _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags);
+
+ /* Changing volume may change the balance and fade values as well */
+ set_balance_fade (control);
+
+ g_object_thaw_notify (G_OBJECT (control));
+}
+
+static MateMixerAppInfo *
+pulse_stream_control_get_app_info (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), NULL);
+
+ return PULSE_STREAM_CONTROL (mmsc)->priv->app_info;
+}
+
+static gboolean
+pulse_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ return PULSE_STREAM_CONTROL_GET_CLASS (mmsc)->set_mute (PULSE_STREAM_CONTROL (mmsc), mute);
+}
+
+static guint
+pulse_stream_control_get_num_channels (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), 0);
+
+ return PULSE_STREAM_CONTROL (mmsc)->priv->channel_map.channels;
+}
+
+static guint
+pulse_stream_control_get_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ return PULSE_STREAM_CONTROL (mmsc)->priv->volume;
+}
+
+static gboolean
+pulse_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume)
+{
+ PulseStreamControl *control;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+ cvolume = control->priv->cvolume;
+
+ if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL)
+ return FALSE;
+
+ return set_cvolume (control, &cvolume);
+}
+
+static gdouble
+pulse_stream_control_get_decibel (MateMixerStreamControl *mmsc)
+{
+ gdouble value;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY);
+
+ value = pa_sw_volume_to_dB (pulse_stream_control_get_volume (mmsc));
+
+ /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */
+ return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
+}
+
+static gboolean
+pulse_stream_control_set_decibel (MateMixerStreamControl *mmsc, gdouble decibel)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ return pulse_stream_control_set_volume (mmsc,
+ pa_sw_volume_from_dB (decibel));
+}
+
+static guint
+pulse_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->cvolume.channels)
+ return (guint) PA_VOLUME_MUTED;
+
+ return (guint) control->priv->cvolume.values[channel];
+}
+
+static gboolean
+pulse_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume)
+{
+ PulseStreamControl *control;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->cvolume.channels)
+ return FALSE;
+
+ /* This is safe, because the cvolume is validated by set_cvolume() */
+ cvolume = control->priv->cvolume;
+ cvolume.values[channel] = (pa_volume_t) volume;
+
+ return set_cvolume (control, &cvolume);
+}
+
+static gdouble
+pulse_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint channel)
+{
+ PulseStreamControl *control;
+ gdouble value;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->cvolume.channels)
+ return -MATE_MIXER_INFINITY;
+
+ value = pa_sw_volume_to_dB (control->priv->cvolume.values[channel]);
+
+ return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
+}
+
+static gboolean
+pulse_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc,
+ guint channel,
+ gdouble decibel)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ return pulse_stream_control_set_channel_volume (mmsc,
+ channel,
+ pa_sw_volume_from_dB (decibel));
+}
+
+static MateMixerChannelPosition
+pulse_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->channel_map.channels)
+ return MATE_MIXER_CHANNEL_UNKNOWN;
+
+ return pulse_convert_position_to_pulse (control->priv->channel_map.map[channel]);
+}
+
+static gboolean
+pulse_stream_control_has_channel_position (MateMixerStreamControl *mmsc,
+ MateMixerChannelPosition position)
+{
+ PulseStreamControl *control;
+ pa_channel_position_t p;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ /* 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 (p == PA_CHANNEL_POSITION_INVALID)
+ return FALSE;
+
+ if (pa_channel_map_has_position (&control->priv->channel_map, p) != 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean
+pulse_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance)
+{
+ PulseStreamControl *control;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+ cvolume = control->priv->cvolume;
+
+ if (pa_cvolume_set_balance (&cvolume, &control->priv->channel_map, balance) == NULL)
+ return FALSE;
+
+ return set_cvolume (control, &cvolume);
+}
+
+static gboolean
+pulse_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade)
+{
+ PulseStreamControl *control;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+ cvolume = control->priv->cvolume;
+
+ if (pa_cvolume_set_fade (&cvolume, &control->priv->channel_map, fade) == NULL)
+ return FALSE;
+
+ return set_cvolume (control, &cvolume);
+}
+
+static gboolean
+pulse_stream_control_get_monitor_enabled (MateMixerStreamControl *mmsc)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (control->priv->monitor != NULL)
+ return pulse_monitor_get_enabled (control->priv->monitor);
+
+ return FALSE;
+}
+
+static gboolean
+pulse_stream_control_set_monitor_enabled (MateMixerStreamControl *mmsc, gboolean enabled)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (enabled == TRUE) {
+ if (control->priv->monitor == NULL) {
+ control->priv->monitor =
+ PULSE_STREAM_CONTROL_GET_CLASS (control)->create_monitor (control);
+
+ if G_UNLIKELY (control->priv->monitor == NULL)
+ return FALSE;
+
+ g_signal_connect (G_OBJECT (control->priv->monitor),
+ "value",
+ G_CALLBACK (on_monitor_value),
+ control);
+ }
+ } else {
+ if (control->priv->monitor == NULL)
+ return FALSE;
+ }
+ return pulse_monitor_set_enabled (control->priv->monitor, enabled);
+}
+
+static guint
+pulse_stream_control_get_min_volume (MateMixerStreamControl *mmsc)
+{
+ return (guint) PA_VOLUME_MUTED;
+}
+
+static guint
+pulse_stream_control_get_max_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ return (guint) PA_VOLUME_UI_MAX;
+}
+
+static guint
+pulse_stream_control_get_normal_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ return (guint) PA_VOLUME_NORM;
+}
+
+static guint
+pulse_stream_control_get_base_volume (MateMixerStreamControl *mmsc)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (control->priv->base_volume > 0)
+ return control->priv->base_volume;
+ else
+ return (guint) PA_VOLUME_NORM;
+}
+
+static void
+on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStreamControl *control)
+{
+ g_signal_emit_by_name (G_OBJECT (control),
+ "monitor-value",
+ value);
+}
+
+static void
+set_balance_fade (PulseStreamControl *control)
+{
+ gfloat value;
+
+ /* PulseAudio returns the default 0.0f value on error, so skip checking validity
+ * of the channel map and cvolume */
+ value = pa_cvolume_get_balance (&control->priv->cvolume,
+ &control->priv->channel_map);
+
+ _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), value);
+
+ value = pa_cvolume_get_fade (&control->priv->cvolume,
+ &control->priv->channel_map);
+
+ _mate_mixer_stream_control_set_fade (MATE_MIXER_STREAM_CONTROL (control), value);
+}
+
+static gboolean
+set_cvolume (PulseStreamControl *control, pa_cvolume *cvolume)
+{
+ PulseStreamControlClass *klass;
+
+ if (pa_cvolume_valid (cvolume) == 0)
+ return FALSE;
+ if (pa_cvolume_equal (cvolume, &control->priv->cvolume) != 0)
+ return TRUE;
+
+ klass = PULSE_STREAM_CONTROL_GET_CLASS (control);
+
+ if (klass->set_volume (control, cvolume) == FALSE)
+ return FALSE;
+
+ control->priv->cvolume = *cvolume;
+ control->priv->volume = (guint) pa_cvolume_max (cvolume);
+
+ g_object_notify (G_OBJECT (control), "volume");
+
+ /* Changing volume may change the balance and fade values as well */
+ set_balance_fade (control);
+ return TRUE;
+}