summaryrefslogtreecommitdiff
path: root/backends/pulse/pulse-monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/pulse/pulse-monitor.c')
-rw-r--r--backends/pulse/pulse-monitor.c303
1 files changed, 202 insertions, 101 deletions
diff --git a/backends/pulse/pulse-monitor.c b/backends/pulse/pulse-monitor.c
index 041f903..3d5b4a8 100644
--- a/backends/pulse/pulse-monitor.c
+++ b/backends/pulse/pulse-monitor.c
@@ -34,23 +34,44 @@ struct _PulseMonitorPrivate
};
enum {
+ PROP_0,
+ PROP_ENABLED,
+ PROP_NAME,
+ PROP_INDEX_SOURCE,
+ PROP_INDEX_SINK_INPUT,
+ N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+enum {
VALUE,
N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0, };
-static void pulse_monitor_class_init (PulseMonitorClass *klass);
-static void pulse_monitor_init (PulseMonitor *port);
-static void pulse_monitor_finalize (GObject *object);
+static void pulse_monitor_class_init (PulseMonitorClass *klass);
+
+static void pulse_monitor_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_monitor_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void pulse_monitor_init (PulseMonitor *monitor);
+static void pulse_monitor_finalize (GObject *object);
G_DEFINE_TYPE (PulseMonitor, pulse_monitor, G_TYPE_OBJECT);
-static gboolean monitor_prepare (PulseMonitor *monitor);
-static gboolean monitor_connect_record (PulseMonitor *monitor);
-static void monitor_read_cb (pa_stream *stream,
- size_t length,
- void *userdata);
+static gboolean stream_connect (PulseMonitor *monitor);
+
+static void stream_read_cb (pa_stream *stream,
+ size_t length,
+ void *userdata);
static void
pulse_monitor_class_init (PulseMonitorClass *klass)
@@ -58,7 +79,50 @@ pulse_monitor_class_init (PulseMonitorClass *klass)
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = pulse_monitor_finalize;
+ object_class->finalize = pulse_monitor_finalize;
+ object_class->get_property = pulse_monitor_get_property;
+ object_class->set_property = pulse_monitor_set_property;
+
+ properties[PROP_ENABLED] =
+ g_param_spec_boolean ("enabled",
+ "Enabled",
+ "Monitor enabled",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_NAME] =
+ g_param_spec_string ("name",
+ "Name",
+ "Name of the monitor",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_INDEX_SOURCE] =
+ g_param_spec_uint ("index-source",
+ "Index of source",
+ "Index of the PulseAudio source",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_INDEX_SINK_INPUT] =
+ g_param_spec_uint ("index-sink-input",
+ "Index of sink input",
+ "Index of the PulseAudio sink input",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
signals[VALUE] =
g_signal_new ("value",
@@ -76,6 +140,61 @@ pulse_monitor_class_init (PulseMonitorClass *klass)
}
static void
+pulse_monitor_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PulseMonitor *monitor;
+
+ monitor = PULSE_MONITOR (object);
+
+ switch (param_id) {
+ case PROP_ENABLED:
+ g_value_set_boolean (value, monitor->priv->enabled);
+ break;
+ case PROP_NAME:
+ g_value_set_string (value, monitor->priv->name);
+ break;
+ case PROP_INDEX_SOURCE:
+ g_value_set_uint (value, monitor->priv->index_source);
+ break;
+ case PROP_INDEX_SINK_INPUT:
+ g_value_set_uint (value, monitor->priv->index_sink_input);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_monitor_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PulseMonitor *monitor;
+
+ monitor = PULSE_MONITOR (object);
+
+ switch (param_id) {
+ case PROP_NAME:
+ pulse_monitor_set_name (monitor, g_value_get_string (value));
+ break;
+ case PROP_INDEX_SOURCE:
+ monitor->priv->index_source = g_value_get_uint (value);
+ break;
+ case PROP_INDEX_SINK_INPUT:
+ monitor->priv->index_sink_input = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
pulse_monitor_init (PulseMonitor *monitor)
{
monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
@@ -90,70 +209,46 @@ pulse_monitor_finalize (GObject *object)
monitor = PULSE_MONITOR (object);
- if (monitor->priv->stream)
+ /* The pulse stream may exist if the monitor is running */
+ if (monitor->priv->stream != NULL) {
+ pa_stream_disconnect (monitor->priv->stream);
pa_stream_unref (monitor->priv->stream);
+ }
pa_context_unref (monitor->priv->context);
pa_proplist_free (monitor->priv->proplist);
+ g_free (monitor->priv->name);
+
G_OBJECT_CLASS (pulse_monitor_parent_class)->finalize (object);
}
PulseMonitor *
pulse_monitor_new (pa_context *context,
pa_proplist *proplist,
+ const gchar *name,
guint32 index_source,
guint32 index_sink_input)
{
PulseMonitor *monitor;
- monitor = g_object_new (PULSE_TYPE_MONITOR, NULL);
+ g_return_val_if_fail (context != NULL, NULL);
+ g_return_val_if_fail (proplist != NULL, NULL);
+
+ monitor = g_object_new (PULSE_TYPE_MONITOR,
+ "name", name,
+ "index-source", index_source,
+ "index-sink-input", index_sink_input,
+ NULL);
monitor->priv->context = pa_context_ref (context);
monitor->priv->proplist = pa_proplist_copy (proplist);
- monitor->priv->index_source = index_source;
- monitor->priv->index_sink_input = index_sink_input;
-
return monitor;
}
gboolean
-pulse_monitor_enable (PulseMonitor *monitor)
-{
- g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
-
- if (!monitor->priv->enabled) {
- if (monitor->priv->stream == NULL)
- monitor_prepare (monitor);
-
- if (G_LIKELY (monitor->priv->stream != NULL))
- monitor->priv->enabled = monitor_connect_record (monitor);
- }
-
- return monitor->priv->enabled;
-}
-
-void
-pulse_monitor_disable (PulseMonitor *monitor)
-{
- g_return_if_fail (PULSE_IS_MONITOR (monitor));
-
- if (!monitor->priv->enabled)
- return;
-
- pa_stream_disconnect (monitor->priv->stream);
-
- // XXX stream must be destroyed on disable, re-enabling does not work, this
- // is just a quick temporary solution
- pa_stream_unref (monitor->priv->stream);
- monitor->priv->stream = NULL;
-
- monitor->priv->enabled = FALSE;
-}
-
-gboolean
-pulse_monitor_is_enabled (PulseMonitor *monitor)
+pulse_monitor_get_enabled (PulseMonitor *monitor)
{
g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
@@ -161,30 +256,27 @@ pulse_monitor_is_enabled (PulseMonitor *monitor)
}
gboolean
-pulse_monitor_update_index (PulseMonitor *monitor,
- guint32 index_source,
- guint32 index_sink_input)
+pulse_monitor_set_enabled (PulseMonitor *monitor, gboolean enabled)
{
g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
- if (monitor->priv->index_source == index_source &&
- monitor->priv->index_sink_input == index_sink_input)
+ if (enabled == monitor->priv->enabled)
return TRUE;
- monitor->priv->index_source = index_source;
- monitor->priv->index_sink_input = index_sink_input;
-
- if (pulse_monitor_is_enabled (monitor)) {
- pulse_monitor_disable (monitor);
+ if (enabled) {
+ monitor->priv->enabled = stream_connect (monitor);
- /* Unset the Pulse stream to let enabling recreate it */
- g_clear_pointer (&monitor->priv->stream, pa_stream_unref);
+ if (monitor->priv->enabled == FALSE)
+ return FALSE;
+ } else {
+ pa_stream_disconnect (monitor->priv->stream);
+ pa_stream_unref (monitor->priv->stream);
- pulse_monitor_enable (monitor);
- } else if (monitor->priv->stream) {
- /* Disabled now but was enabled before and still holds source index */
- g_clear_pointer (&monitor->priv->stream, pa_stream_unref);
+ monitor->priv->stream = NULL;
+ monitor->priv->enabled = FALSE;
}
+ g_object_notify_by_pspec (G_OBJECT (monitor), properties[PROP_ENABLED]);
+
return TRUE;
}
@@ -201,21 +293,32 @@ pulse_monitor_set_name (PulseMonitor *monitor, const gchar *name)
{
g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
- g_free (monitor->priv->name);
+ if (g_strcmp0 (name, monitor->priv->name) != 0) {
+ g_free (monitor->priv->name);
+ monitor->priv->name = g_strdup (name);
- monitor->priv->name = g_strdup (name);
+ g_object_notify_by_pspec (G_OBJECT (monitor), properties[PROP_NAME]);
+ }
return TRUE;
}
static gboolean
-monitor_prepare (PulseMonitor *monitor)
+stream_connect (PulseMonitor *monitor)
{
pa_sample_spec spec;
+ pa_buffer_attr attr;
const gchar *name;
+ gchar *idx;
+ int ret;
- spec.channels = 1;
- spec.format = PA_SAMPLE_FLOAT32;
- spec.rate = 25;
+ attr.maxlength = (guint32) -1;
+ attr.tlength = 0;
+ attr.prebuf = 0;
+ attr.minreq = 0;
+ attr.fragsize = sizeof (gfloat);
+ spec.channels = 1;
+ spec.format = PA_SAMPLE_FLOAT32;
+ spec.rate = 25;
if (monitor->priv->name != NULL)
name = monitor->priv->name;
@@ -230,60 +333,58 @@ monitor_prepare (PulseMonitor *monitor)
monitor->priv->proplist);
if (G_UNLIKELY (monitor->priv->stream == NULL)) {
- g_warning ("Failed to create PulseAudio monitor: %s",
+ g_warning ("Failed to create peak monitor: %s",
pa_strerror (pa_context_errno (monitor->priv->context)));
return FALSE;
}
+ /* Set sink input index for the stream, source outputs are not supported */
if (monitor->priv->index_sink_input != PA_INVALID_INDEX)
pa_stream_set_monitor_stream (monitor->priv->stream,
monitor->priv->index_sink_input);
pa_stream_set_read_callback (monitor->priv->stream,
- monitor_read_cb,
+ stream_read_cb,
monitor);
- return TRUE;
-}
-
-static gboolean
-monitor_connect_record (PulseMonitor *monitor)
-{
- pa_buffer_attr attr;
- gchar *name;
- int ret;
- attr.maxlength = (guint32) -1;
- attr.tlength = 0;
- attr.prebuf = 0;
- attr.minreq = 0;
- attr.fragsize = sizeof (gfloat);
-
- name = g_strdup_printf ("%u", monitor->priv->index_source);
- ret = pa_stream_connect_record (monitor->priv->stream,
- name,
- &attr,
- PA_STREAM_DONT_MOVE |
- PA_STREAM_PEAK_DETECT |
- PA_STREAM_ADJUST_LATENCY |
- PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND);
- g_free (name);
+ /* Source index must be passed as a string */
+ idx = g_strdup_printf ("%u", monitor->priv->index_source);
+ ret = pa_stream_connect_record (monitor->priv->stream,
+ idx,
+ &attr,
+ PA_STREAM_DONT_MOVE |
+ PA_STREAM_PEAK_DETECT |
+ PA_STREAM_ADJUST_LATENCY);
+ g_free (idx);
if (ret < 0) {
- g_warning ("Failed to connect PulseAudio monitor: %s", pa_strerror (ret));
+ g_warning ("Failed to connect peak monitor: %s", pa_strerror (ret));
return FALSE;
}
return TRUE;
}
static void
-monitor_read_cb (pa_stream *stream, size_t length, void *userdata)
+stream_read_cb (pa_stream *stream, size_t length, void *userdata)
{
const void *data;
+ /* Read the next fragment from the buffer (for recording streams).
+ *
+ * If there is data at the current read index, data will point to the
+ * actual data and length will contain the size of the data in bytes
+ * (which can be less or more than a complete fragment).
+ *
+ * If there is no data at the current read index, it means that either
+ * the buffer is empty or it contains a hole (that is, the write index
+ * is ahead of the read index but there's no data where the read index
+ * points at). If the buffer is empty, data will be NULL and length will
+ * be 0. If there is a hole, data will be NULL and length will contain
+ * the length of the hole. */
if (pa_stream_peek (stream, &data, &length) < 0)
return;
- if (data) {
+ if (data != NULL) {
gdouble v = ((const gfloat *) data)[length / sizeof (gfloat) - 1];
g_signal_emit (G_OBJECT (userdata),
@@ -294,6 +395,6 @@ monitor_read_cb (pa_stream *stream, size_t length, void *userdata)
/* pa_stream_drop() should not be called if the buffer is empty, but it
* should be called if there is a hole */
- if (length)
+ if (length > 0)
pa_stream_drop (stream);
}