summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/pulse/pulse-ext-stream.c522
-rw-r--r--backends/pulse/pulse-ext-stream.h4
-rw-r--r--docs/reference/libmatemixer-sections.txt10
-rw-r--r--libmatemixer/matemixer-stored-control.c108
-rw-r--r--libmatemixer/matemixer-stored-control.h41
5 files changed, 553 insertions, 132 deletions
diff --git a/backends/pulse/pulse-ext-stream.c b/backends/pulse/pulse-ext-stream.c
index 5d2ec75..5d7863a 100644
--- a/backends/pulse/pulse-ext-stream.c
+++ b/backends/pulse/pulse-ext-stream.c
@@ -33,16 +33,20 @@
struct _PulseExtStreamPrivate
{
- MateMixerAppInfo *app_info;
- MateMixerDirection direction;
+ guint volume;
+ pa_cvolume cvolume;
+ pa_channel_map channel_map;
+ MateMixerAppInfo *app_info;
+ PulseConnection *connection;
};
enum {
PROP_0,
- PROP_DIRECTION
+ PROP_CONNECTION,
+ N_PROPERTIES
};
-static void mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface);
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
static void pulse_ext_stream_class_init (PulseExtStreamClass *klass);
@@ -56,52 +60,95 @@ static void pulse_ext_stream_set_property (GObject *object,
GParamSpec *pspec);
static void pulse_ext_stream_init (PulseExtStream *ext);
+static void pulse_ext_stream_dispose (GObject *object);
+static void pulse_ext_stream_finalize (GObject *object);
-G_DEFINE_TYPE_WITH_CODE (PulseExtStream, pulse_ext_stream, PULSE_TYPE_STREAM_CONTROL,
- G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STORED_CONTROL,
- mate_mixer_stored_control_interface_init))
+G_DEFINE_TYPE (PulseExtStream, pulse_ext_stream, MATE_MIXER_TYPE_STORED_CONTROL)
-static MateMixerDirection pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc);
+static MateMixerAppInfo * pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc);
-static MateMixerAppInfo * pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc);
+static gboolean pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc,
+ MateMixerStream *mms);
-static gboolean pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc,
- MateMixerStream *mms);
+static gboolean pulse_ext_stream_set_mute (MateMixerStreamControl *mmsc,
+ gboolean mute);
-static gboolean pulse_ext_stream_set_mute (PulseStreamControl *control,
- gboolean mute);
-static gboolean pulse_ext_stream_set_volume (PulseStreamControl *control,
- pa_cvolume *cvolume);
+static guint pulse_ext_stream_get_num_channels (MateMixerStreamControl *mmsc);
-static void fill_ext_stream_restore_info (PulseStreamControl *control,
- pa_ext_stream_restore_info *info);
+static guint pulse_ext_stream_get_volume (MateMixerStreamControl *mmsc);
+static gboolean pulse_ext_stream_set_volume (MateMixerStreamControl *mmsc,
+ guint volume);
-static void
-mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface)
-{
- iface->get_direction = pulse_ext_stream_get_direction;
-}
+static guint pulse_ext_stream_get_channel_volume (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean pulse_ext_stream_set_channel_volume (MateMixerStreamControl *mmsc,
+ guint channel,
+ guint volume);
+
+static MateMixerChannelPosition pulse_ext_stream_get_channel_position (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean pulse_ext_stream_has_channel_position (MateMixerStreamControl *mmsc,
+ MateMixerChannelPosition position);
+
+static gboolean pulse_ext_stream_set_balance (MateMixerStreamControl *mmsc,
+ gfloat balance);
+
+static gboolean pulse_ext_stream_set_fade (MateMixerStreamControl *mmsc,
+ gfloat fade);
+
+static guint pulse_ext_stream_get_min_volume (MateMixerStreamControl *mmsc);
+static guint pulse_ext_stream_get_max_volume (MateMixerStreamControl *mmsc);
+static guint pulse_ext_stream_get_normal_volume (MateMixerStreamControl *mmsc);
+static guint pulse_ext_stream_get_base_volume (MateMixerStreamControl *mmsc);
+
+static void fill_ext_stream_restore_info (PulseExtStream *ext,
+ pa_ext_stream_restore_info *info);
+
+static gboolean write_cvolume (PulseExtStream *ext,
+ const pa_cvolume *cvolume);
+static void store_cvolume (PulseExtStream *ext,
+ const pa_cvolume *cvolume);
static void
pulse_ext_stream_class_init (PulseExtStreamClass *klass)
{
GObjectClass *object_class;
MateMixerStreamControlClass *control_class;
- PulseStreamControlClass *pulse_class;
object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_ext_stream_dispose;
+ object_class->finalize = pulse_ext_stream_finalize;
object_class->get_property = pulse_ext_stream_get_property;
object_class->set_property = pulse_ext_stream_set_property;
control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
- control_class->get_app_info = pulse_ext_stream_get_app_info;
- control_class->set_stream = pulse_ext_stream_set_stream;
-
- pulse_class = PULSE_STREAM_CONTROL_CLASS (klass);
- pulse_class->set_mute = pulse_ext_stream_set_mute;
- pulse_class->set_volume = pulse_ext_stream_set_volume;
-
- g_object_class_override_property (object_class, PROP_DIRECTION, "direction");
+ control_class->get_app_info = pulse_ext_stream_get_app_info;
+ control_class->set_stream = pulse_ext_stream_set_stream;
+ control_class->set_mute = pulse_ext_stream_set_mute;
+ control_class->get_num_channels = pulse_ext_stream_get_num_channels;
+ control_class->get_volume = pulse_ext_stream_get_volume;
+ control_class->set_volume = pulse_ext_stream_set_volume;
+ control_class->get_channel_volume = pulse_ext_stream_get_channel_volume;
+ control_class->set_channel_volume = pulse_ext_stream_set_channel_volume;
+ control_class->get_channel_position = pulse_ext_stream_get_channel_position;
+ control_class->has_channel_position = pulse_ext_stream_has_channel_position;
+ control_class->set_balance = pulse_ext_stream_set_balance;
+ control_class->set_fade = pulse_ext_stream_set_fade;
+ control_class->get_min_volume = pulse_ext_stream_get_min_volume;
+ control_class->get_max_volume = pulse_ext_stream_get_max_volume;
+ control_class->get_normal_volume = pulse_ext_stream_get_normal_volume;
+ control_class->get_base_volume = pulse_ext_stream_get_base_volume;
+
+ 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 (PulseExtStreamPrivate));
}
@@ -117,8 +164,8 @@ pulse_ext_stream_get_property (GObject *object,
ext = PULSE_EXT_STREAM (object);
switch (param_id) {
- case PROP_DIRECTION:
- g_value_set_enum (value, ext->priv->direction);
+ case PROP_CONNECTION:
+ g_value_set_object (value, ext->priv->connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -137,8 +184,9 @@ pulse_ext_stream_set_property (GObject *object,
ext = PULSE_EXT_STREAM (object);
switch (param_id) {
- case PROP_DIRECTION:
- ext->priv->direction = g_value_get_enum (value);
+ case PROP_CONNECTION:
+ /* Construct-only object */
+ ext->priv->connection = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -154,26 +202,59 @@ pulse_ext_stream_init (PulseExtStream *ext)
PulseExtStreamPrivate);
}
+static void
+pulse_ext_stream_dispose (GObject *object)
+{
+ PulseExtStream *ext;
+
+ ext = PULSE_EXT_STREAM (object);
+
+ g_clear_object (&ext->priv->connection);
+
+ G_OBJECT_CLASS (pulse_ext_stream_parent_class)->dispose (object);
+}
+
+static void
+pulse_ext_stream_finalize (GObject *object)
+{
+ PulseExtStream *ext;
+
+ ext = PULSE_EXT_STREAM (object);
+
+ if (ext->priv->app_info != NULL)
+ _mate_mixer_app_info_free (ext->priv->app_info);
+
+ G_OBJECT_CLASS (pulse_ext_stream_parent_class)->finalize (object);
+}
+
PulseExtStream *
pulse_ext_stream_new (PulseConnection *connection,
const pa_ext_stream_restore_info *info,
PulseStream *parent)
{
- PulseExtStream *ext;
- gchar *suffix;
- MateMixerDirection direction;
- MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
- MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE |
- MATE_MIXER_STREAM_CONTROL_MUTE_READABLE |
- MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE;
- MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
- MateMixerAppInfo *app_info;
-
- MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN;
+ PulseExtStream *ext;
+ gchar *suffix;
+ MateMixerAppInfo *app_info = NULL;
+ MateMixerDirection direction;
+ MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MOVABLE |
+ MATE_MIXER_STREAM_CONTROL_STORED;
+ MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
+ MateMixerStreamControlMediaRole media_role =
+ MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN;
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
g_return_val_if_fail (info != NULL, NULL);
+ /* The name of an ext-stream is in one of the following formats:
+ * sink-input-by-media-role: ...
+ * sink-input-by-application-name: ...
+ * sink-input-by-application-id: ...
+ * sink-input-by-media-name: ...
+ * source-output-by-media-role: ...
+ * source-output-by-application-name: ...
+ * source-output-by-application-id: ...
+ * source-output-by-media-name: ...
+ */
if (g_str_has_prefix (info->name, "sink-input"))
direction = MATE_MIXER_DIRECTION_OUTPUT;
else if (g_str_has_prefix (info->name, "source-output"))
@@ -181,26 +262,36 @@ pulse_ext_stream_new (PulseConnection *connection,
else
direction = MATE_MIXER_DIRECTION_UNKNOWN;
- app_info = _mate_mixer_app_info_new ();
-
suffix = strchr (info->name, ':');
if (suffix != NULL)
suffix++;
if (strstr (info->name, "-by-media-role:")) {
- if (G_LIKELY (suffix != NULL))
+ if G_LIKELY (suffix != NULL)
media_role = pulse_convert_media_role_name (suffix);
}
else if (strstr (info->name, "-by-application-name:")) {
role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION;
- if (G_LIKELY (suffix != NULL))
+ /* Make sure an application ext-stream always has a MateMixerAppInfo
+ * structure available, even in the case no application info is
+ * available */
+ if (app_info == NULL)
+ app_info = _mate_mixer_app_info_new ();
+
+ if G_LIKELY (suffix != NULL)
_mate_mixer_app_info_set_name (app_info, suffix);
}
else if (strstr (info->name, "-by-application-id:")) {
role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION;
- if (G_LIKELY (suffix != NULL))
+ /* Make sure an application ext-stream always has a MateMixerAppInfo
+ * structure available, even in the case no application info is
+ * available */
+ if (app_info == NULL)
+ app_info = _mate_mixer_app_info_new ();
+
+ if G_LIKELY (suffix != NULL)
_mate_mixer_app_info_set_id (app_info, suffix);
}
@@ -214,12 +305,12 @@ pulse_ext_stream_new (PulseConnection *connection,
"stream", parent,
NULL);
- if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION)
- ext->priv->app_info = app_info;
- else
- _mate_mixer_app_info_free (app_info);
+ // XXX property?
+ ext->priv->app_info = app_info;
+ /* Store values which are expected to be changed */
pulse_ext_stream_update (ext, info, parent);
+
return ext;
}
@@ -228,6 +319,9 @@ pulse_ext_stream_update (PulseExtStream *ext,
const pa_ext_stream_restore_info *info,
PulseStream *parent)
{
+ MateMixerStreamControlFlags flags;
+ gboolean volume_changed = FALSE;
+
g_return_if_fail (PULSE_IS_EXT_STREAM (ext));
g_return_if_fail (info != NULL);
@@ -237,27 +331,54 @@ pulse_ext_stream_update (PulseExtStream *ext,
_mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (ext),
info->mute ? TRUE : FALSE);
- pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (ext),
- &info->channel_map);
+ flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (ext));
+
+ if (pa_channel_map_valid (&info->channel_map) != 0) {
+ if (pa_channel_map_can_balance (&info->channel_map) != 0)
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+ else
+ flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+
+ if (pa_channel_map_can_fade (&info->channel_map) != 0)
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+ else
+ flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+
+ ext->priv->channel_map = info->channel_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 (&ext->priv->channel_map);
+ }
- pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (ext),
- &info->volume,
- 0);
+ if (pa_cvolume_valid (&info->volume) != 0) {
+ flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE;
+ if (pa_cvolume_equal (&ext->priv->cvolume, &info->volume) == 0)
+ volume_changed = TRUE;
+ } else {
+ flags &= ~(MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE);
+
+ if (ext->priv->volume != (guint) PA_VOLUME_MUTED)
+ volume_changed = TRUE;
+ }
+
+ if (volume_changed == TRUE)
+ store_cvolume (ext, &info->volume);
+
+ _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (ext), flags);
+
+ /* Also set initially, but may change at any time */
_mate_mixer_stream_control_set_stream (MATE_MIXER_STREAM_CONTROL (ext),
MATE_MIXER_STREAM (parent));
g_object_thaw_notify (G_OBJECT (ext));
}
-static MateMixerDirection
-pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc)
-{
- g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), MATE_MIXER_DIRECTION_UNKNOWN);
-
- return PULSE_EXT_STREAM (mmsc)->priv->direction;
-}
-
static MateMixerAppInfo *
pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc)
{
@@ -269,82 +390,277 @@ pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc)
static gboolean
pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc, MateMixerStream *mms)
{
+ PulseExtStream *ext;
pa_ext_stream_restore_info info;
g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE);
g_return_val_if_fail (mms == NULL || PULSE_IS_STREAM (mms), FALSE);
- fill_ext_stream_restore_info (PULSE_STREAM_CONTROL (mmsc), &info);
+ ext = PULSE_EXT_STREAM (mmsc);
+ fill_ext_stream_restore_info (ext, &info);
if (mms != NULL)
info.device = mate_mixer_stream_get_name (mms);
else
info.device = NULL;
- return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (PULSE_STREAM_CONTROL (mmsc)),
- &info);
+ return pulse_connection_write_ext_stream (ext->priv->connection, &info);
}
static gboolean
-pulse_ext_stream_set_mute (PulseStreamControl *control, gboolean mute)
+pulse_ext_stream_set_mute (MateMixerStreamControl *mmsc, gboolean mute)
{
+ PulseExtStream *ext;
pa_ext_stream_restore_info info;
- g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE);
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE);
- fill_ext_stream_restore_info (control, &info);
+ ext = PULSE_EXT_STREAM (mmsc);
+ fill_ext_stream_restore_info (ext, &info);
info.mute = mute;
- return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control),
- &info);
+ return pulse_connection_write_ext_stream (ext->priv->connection, &info);
+}
+
+static guint
+pulse_ext_stream_get_num_channels (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), 0);
+
+ return PULSE_EXT_STREAM (mmsc)->priv->channel_map.channels;
+}
+
+static guint
+pulse_ext_stream_get_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED);
+
+ return PULSE_EXT_STREAM (mmsc)->priv->volume;
}
static gboolean
-pulse_ext_stream_set_volume (PulseStreamControl *control, pa_cvolume *cvolume)
+pulse_ext_stream_set_volume (MateMixerStreamControl *mmsc, guint volume)
{
- pa_ext_stream_restore_info info;
+ PulseExtStream *ext;
+ pa_cvolume cvolume;
- g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE);
- g_return_val_if_fail (cvolume != NULL, FALSE);
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE);
- fill_ext_stream_restore_info (control, &info);
+ ext = PULSE_EXT_STREAM (mmsc);
+ cvolume = ext->priv->cvolume;
- info.volume = *cvolume;
+ /* Modify a temporary cvolume structure as the change may be irreversible */
+ if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL)
+ return FALSE;
+
+ return write_cvolume (ext, &cvolume);
+}
+
+static guint
+pulse_ext_stream_get_channel_volume (MateMixerStreamControl *mmsc, guint channel)
+{
+ PulseExtStream *ext;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED);
+
+ ext = PULSE_EXT_STREAM (mmsc);
+
+ if (channel >= ext->priv->cvolume.channels)
+ return (guint) PA_VOLUME_MUTED;
+
+ return (guint) ext->priv->cvolume.values[channel];
+}
+
+static gboolean
+pulse_ext_stream_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume)
+{
+ PulseExtStream *ext;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE);
+
+ ext = PULSE_EXT_STREAM (mmsc);
- return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control),
- &info);
+ if (channel >= ext->priv->cvolume.channels)
+ return FALSE;
+
+ /* Modify a temporary cvolume structure as the change may be irreversible */
+ cvolume = ext->priv->cvolume;
+ cvolume.values[channel] = (pa_volume_t) volume;
+
+ return write_cvolume (ext, &cvolume);
+}
+
+static MateMixerChannelPosition
+pulse_ext_stream_get_channel_position (MateMixerStreamControl *mmsc, guint channel)
+{
+ PulseExtStream *ext;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), MATE_MIXER_CHANNEL_UNKNOWN);
+
+ ext = PULSE_EXT_STREAM (mmsc);
+
+ if (channel >= ext->priv->channel_map.channels)
+ return MATE_MIXER_CHANNEL_UNKNOWN;
+
+ return pulse_convert_position_from_pulse (ext->priv->channel_map.map[channel]);
+}
+
+static gboolean
+pulse_ext_stream_has_channel_position (MateMixerStreamControl *mmsc,
+ MateMixerChannelPosition position)
+{
+ PulseExtStream *ext;
+ pa_channel_position_t p;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE);
+
+ ext = PULSE_EXT_STREAM (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 (&ext->priv->channel_map, p) != 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean
+pulse_ext_stream_set_balance (MateMixerStreamControl *mmsc, gfloat balance)
+{
+ PulseExtStream *ext;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE);
+
+ ext = PULSE_EXT_STREAM (mmsc);
+ cvolume = ext->priv->cvolume;
+
+ /* Modify a temporary cvolume structure as the change may be irreversible */
+ if (pa_cvolume_set_balance (&cvolume, &ext->priv->channel_map, balance) == NULL)
+ return FALSE;
+
+ return write_cvolume (ext, &cvolume);
+}
+
+static gboolean
+pulse_ext_stream_set_fade (MateMixerStreamControl *mmsc, gfloat fade)
+{
+ PulseExtStream *ext;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE);
+
+ ext = PULSE_EXT_STREAM (mmsc);
+ cvolume = ext->priv->cvolume;
+
+ /* Modify a temporary cvolume structure as the change may be irreversible */
+ if (pa_cvolume_set_fade (&cvolume, &ext->priv->channel_map, fade) == NULL)
+ return FALSE;
+
+ return write_cvolume (ext, &cvolume);
+}
+
+static guint
+pulse_ext_stream_get_min_volume (MateMixerStreamControl *mmsc)
+{
+ return (guint) PA_VOLUME_MUTED;
+}
+
+static guint
+pulse_ext_stream_get_max_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED);
+
+ return (guint) PA_VOLUME_UI_MAX;
+}
+
+static guint
+pulse_ext_stream_get_normal_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED);
+
+ return (guint) PA_VOLUME_NORM;
+}
+
+static guint
+pulse_ext_stream_get_base_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), (guint) PA_VOLUME_MUTED);
+
+ /* Base volume is not supported/used in ext-streams */
+ return (guint) PA_VOLUME_NORM;
}
static void
-fill_ext_stream_restore_info (PulseStreamControl *control,
+fill_ext_stream_restore_info (PulseExtStream *ext,
pa_ext_stream_restore_info *info)
{
- MateMixerStream *stream;
+ MateMixerStream *mms;
MateMixerStreamControl *mmsc;
- const pa_channel_map *map;
- const pa_cvolume *cvolume;
- mmsc = MATE_MIXER_STREAM_CONTROL (control);
+ mmsc = MATE_MIXER_STREAM_CONTROL (ext);
info->name = mate_mixer_stream_control_get_name (mmsc);
info->mute = mate_mixer_stream_control_get_mute (mmsc);
+ info->volume = ext->priv->cvolume;
+ info->channel_map = ext->priv->channel_map;
- map = pulse_stream_control_get_channel_map (control);
- if G_LIKELY (map != NULL)
- info->channel_map = *map;
+ mms = mate_mixer_stream_control_get_stream (mmsc);
+ if (mms != NULL)
+ info->device = mate_mixer_stream_get_name (mms);
else
- pa_channel_map_init (&info->channel_map);
+ info->device = NULL;
+}
- cvolume = pulse_stream_control_get_cvolume (control);
- if G_LIKELY (cvolume != NULL)
- info->volume = *cvolume;
- else
- pa_cvolume_init (&info->volume);
+static gboolean
+write_cvolume (PulseExtStream *ext, const pa_cvolume *cvolume)
+{
+ pa_ext_stream_restore_info info;
- stream = mate_mixer_stream_control_get_stream (mmsc);
- if (stream != NULL)
- info->device = mate_mixer_stream_get_name (stream);
- else
- info->device = NULL;
+ /* Make sure to only store a valid and modified volume */
+ if (pa_cvolume_valid (cvolume) == 0)
+ return FALSE;
+ if (pa_cvolume_equal (cvolume, &ext->priv->cvolume) != 0)
+ return TRUE;
+
+ fill_ext_stream_restore_info (ext, &info);
+ info.volume = *cvolume;
+
+ if (pulse_connection_write_ext_stream (ext->priv->connection, &info) == FALSE)
+ return FALSE;
+
+ store_cvolume (ext, cvolume);
+ return TRUE;
+}
+
+static void
+store_cvolume (PulseExtStream *ext, const pa_cvolume *cvolume)
+{
+ gfloat value;
+
+ /* Avoid validating whether the volume has changed, it should be done by
+ * the caller */
+ ext->priv->cvolume = *cvolume;
+ ext->priv->volume = (guint) pa_cvolume_max (cvolume);
+
+ g_object_notify (G_OBJECT (ext), "volume");
+
+ /* PulseAudio returns the default 0.0f value on error, so skip checking validity
+ * of the channel map and cvolume */
+ value = pa_cvolume_get_balance (&ext->priv->cvolume,
+ &ext->priv->channel_map);
+
+ _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (ext), value);
+
+ value = pa_cvolume_get_fade (&ext->priv->cvolume,
+ &ext->priv->channel_map);
+
+ _mate_mixer_stream_control_set_fade (MATE_MIXER_STREAM_CONTROL (ext), value);
}
diff --git a/backends/pulse/pulse-ext-stream.h b/backends/pulse/pulse-ext-stream.h
index b667dc7..af3d75f 100644
--- a/backends/pulse/pulse-ext-stream.h
+++ b/backends/pulse/pulse-ext-stream.h
@@ -47,7 +47,7 @@ typedef struct _PulseExtStreamPrivate PulseExtStreamPrivate;
struct _PulseExtStream
{
- PulseStreamControl parent;
+ MateMixerStoredControl parent;
/*< private >*/
PulseExtStreamPrivate *priv;
@@ -55,7 +55,7 @@ struct _PulseExtStream
struct _PulseExtStreamClass
{
- PulseStreamControlClass parent_class;
+ MateMixerStoredControlClass parent_class;
};
GType pulse_ext_stream_get_type (void) G_GNUC_CONST;
diff --git a/docs/reference/libmatemixer-sections.txt b/docs/reference/libmatemixer-sections.txt
index d3545f6..911d43d 100644
--- a/docs/reference/libmatemixer-sections.txt
+++ b/docs/reference/libmatemixer-sections.txt
@@ -91,14 +91,18 @@ mate_mixer_device_get_type
<SECTION>
<FILE>matemixer-stored-control</FILE>
<TITLE>MateMixerStoredControl</TITLE>
-MateMixerStoredControlInterface
+MateMixerStoredControl
+MateMixerStoredControlClass
mate_mixer_stored_control_get_direction
<SUBSECTION Standard>
-MATE_MIXER_IS_STORED_CONTROL
MATE_MIXER_STORED_CONTROL
-MATE_MIXER_STORED_CONTROL_GET_INTERFACE
+MATE_MIXER_STORED_CONTROL_CLASS
+MATE_MIXER_STORED_CONTROL_GET_CLASS
+MATE_MIXER_IS_STORED_CONTROL
+MATE_MIXER_IS_STORED_CONTROL_CLASS
MATE_MIXER_TYPE_STORED_CONTROL
<SUBSECTION Private>
+MateMixerStoredControlPrivate
mate_mixer_stored_control_get_type
</SECTION>
diff --git a/libmatemixer/matemixer-stored-control.c b/libmatemixer/matemixer-stored-control.c
index eb2a448..d150490 100644
--- a/libmatemixer/matemixer-stored-control.c
+++ b/libmatemixer/matemixer-stored-control.c
@@ -23,20 +23,104 @@
#include "matemixer-stream-control.h"
#include "matemixer-stored-control.h"
-G_DEFINE_INTERFACE (MateMixerStoredControl, mate_mixer_stored_control, MATE_MIXER_TYPE_STREAM_CONTROL)
+struct _MateMixerStoredControlPrivate
+{
+ MateMixerDirection direction;
+};
+
+enum {
+ PROP_0,
+ PROP_DIRECTION,
+ N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+static void mate_mixer_stored_control_class_init (MateMixerStoredControlClass *klass);
+
+static void mate_mixer_stored_control_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void mate_mixer_stored_control_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void mate_mixer_stored_control_init (MateMixerStoredControl *control);
+
+G_DEFINE_ABSTRACT_TYPE (MateMixerStoredControl, mate_mixer_stored_control, MATE_MIXER_TYPE_STREAM_CONTROL)
+
+static void
+mate_mixer_stored_control_class_init (MateMixerStoredControlClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->get_property = mate_mixer_stored_control_get_property;
+ object_class->set_property = mate_mixer_stored_control_set_property;
+
+ properties[PROP_DIRECTION] =
+ g_param_spec_enum ("direction",
+ "Direction",
+ "Direction of the stored control",
+ MATE_MIXER_TYPE_DIRECTION,
+ MATE_MIXER_DIRECTION_UNKNOWN,
+ 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 (MateMixerStoredControlPrivate));
+}
+
+static void
+mate_mixer_stored_control_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MateMixerStoredControl *control;
+
+ control = MATE_MIXER_STORED_CONTROL (object);
+
+ switch (param_id) {
+ case PROP_DIRECTION:
+ g_value_set_enum (value, control->priv->direction);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+mate_mixer_stored_control_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MateMixerStoredControl *control;
+
+ control = MATE_MIXER_STORED_CONTROL (object);
+
+ switch (param_id) {
+ case PROP_DIRECTION:
+ control->priv->direction = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
static void
-mate_mixer_stored_control_default_init (MateMixerStoredControlInterface *iface)
+mate_mixer_stored_control_init (MateMixerStoredControl *control)
{
- g_object_interface_install_property (iface,
- g_param_spec_enum ("direction",
- "Direction",
- "Direction of the stored control",
- MATE_MIXER_TYPE_DIRECTION,
- MATE_MIXER_DIRECTION_UNKNOWN,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
+ control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control,
+ MATE_MIXER_TYPE_STORED_CONTROL,
+ MateMixerStoredControlPrivate);
}
/**
@@ -48,5 +132,5 @@ mate_mixer_stored_control_get_direction (MateMixerStoredControl *control)
{
g_return_val_if_fail (MATE_MIXER_IS_STORED_CONTROL (control), MATE_MIXER_DIRECTION_UNKNOWN);
- return MATE_MIXER_STORED_CONTROL_GET_INTERFACE (control)->get_direction (control);
+ return control->priv->direction;
}
diff --git a/libmatemixer/matemixer-stored-control.h b/libmatemixer/matemixer-stored-control.h
index 09731ef..d21209e 100644
--- a/libmatemixer/matemixer-stored-control.h
+++ b/libmatemixer/matemixer-stored-control.h
@@ -22,33 +22,50 @@
#include <glib-object.h>
#include <libmatemixer/matemixer-enums.h>
+#include <libmatemixer/matemixer-stream-control.h>
#include <libmatemixer/matemixer-types.h>
G_BEGIN_DECLS
-#define MATE_MIXER_TYPE_STORED_CONTROL \
+#define MATE_MIXER_TYPE_STORED_CONTROL \
(mate_mixer_stored_control_get_type ())
-#define MATE_MIXER_STORED_CONTROL(o) \
+#define MATE_MIXER_STORED_CONTROL(o) \
(G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControl))
-#define MATE_MIXER_IS_STORED_CONTROL(o) \
+#define MATE_MIXER_IS_STORED_CONTROL(o) \
(G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_STORED_CONTROL))
-#define MATE_MIXER_STORED_CONTROL_GET_INTERFACE(o) \
- (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControlInterface))
+#define MATE_MIXER_STORED_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControlClass))
+#define MATE_MIXER_IS_STORED_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_STORED_CONTROL))
+#define MATE_MIXER_STORED_CONTROL_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControlClass))
-typedef struct _MateMixerStoredControlInterface MateMixerStoredControlInterface;
+typedef struct _MateMixerStoredControlClass MateMixerStoredControlClass;
+typedef struct _MateMixerStoredControlPrivate MateMixerStoredControlPrivate;
/**
- * MateMixerStoredControlInterface:
- * @parent_iface: The parent interface.
+ * MateMixerStoredControl:
*
- * The interface structure for #MateMixerStoredControl.
+ * The #MateMixerStoredControl structure contains only private data and should only
+ * be accessed using the provided API.
*/
-struct _MateMixerStoredControlInterface
+struct _MateMixerStoredControl
{
- GTypeInterface parent_iface;
+ MateMixerStreamControl object;
/*< private >*/
- MateMixerDirection (*get_direction) (MateMixerStoredControl *control);
+ MateMixerStoredControlPrivate *priv;
+};
+
+/**
+ * MateMixerStoredControlClass:
+ * @parent_class: The parent class.
+ *
+ * The class structure for #MateMixerStoredControl.
+ */
+struct _MateMixerStoredControlClass
+{
+ MateMixerStreamControlClass parent_class;
};
GType mate_mixer_stored_control_get_type (void) G_GNUC_CONST;