summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2006-05-10 23:59:56 +0000
committerLennart Poettering <lennart@poettering.net>2006-05-10 23:59:56 +0000
commit30402d8fc7fe2d07f3cce0f6b2b0e6fcc0f2bc0e (patch)
tree060e32fa793d09f52054381832cf1280a4fc7dfa
parent218ba9bee9f7a1cee34e1963f6d0b2d75499e58e (diff)
implement mixer object
git-svn-id: file:///home/lennart/svn/public/gst-pulse/trunk@19 bb39ca4e-bce3-0310-b5d4-eea78a553289
-rw-r--r--src/Makefile.am5
-rw-r--r--src/plugin.c8
-rw-r--r--src/polypmixer.c194
-rw-r--r--src/polypmixer.h63
-rw-r--r--src/polypmixerctrl.c458
-rw-r--r--src/polypmixerctrl.h148
-rw-r--r--src/polypmixertrack.c59
-rw-r--r--src/polypmixertrack.h56
-rw-r--r--src/polypsink.c5
-rw-r--r--src/polypsrc.c2
10 files changed, 991 insertions, 7 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index bb55802..1010c5f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,7 +23,10 @@ libgstpolyp_la_SOURCES = \
plugin.c \
polypsink.c polypsink.h \
polypsrc.c polypsrc.h \
- polyputil.c polyputil.h
+ polyputil.c polyputil.h \
+ polypmixer.c polypmixer.h \
+ polypmixerctrl.c polypmixerctrl.h \
+ polypmixertrack.c polypmixertrack.h
libgstpolyp_la_CFLAGS = $(GST_CFLAGS) $(POLYP_CFLAGS)
libgstpolyp_la_LIBADD = $(POLYP_LIBS) $(GST_LIBS) -lgstaudio-0.10
diff --git a/src/plugin.c b/src/plugin.c
index f034222..bae4773 100644
--- a/src/plugin.c
+++ b/src/plugin.c
@@ -25,6 +25,7 @@
#include "polypsink.h"
#include "polypsrc.h"
+#include "polypmixer.h"
GST_DEBUG_CATEGORY(polyp_debug);
@@ -35,6 +36,9 @@ static gboolean plugin_init(GstPlugin* plugin) {
if (!gst_element_register(plugin, "polypsrc", GST_RANK_NONE, GST_TYPE_POLYPSRC))
return FALSE;
+
+ if (!gst_element_register(plugin, "polypmixer", GST_RANK_NONE, GST_TYPE_POLYPMIXER))
+ return FALSE;
GST_DEBUG_CATEGORY_INIT(polyp_debug, "polyp", 0, "Polypaudio elements");
return TRUE;
@@ -43,8 +47,8 @@ static gboolean plugin_init(GstPlugin* plugin) {
GST_PLUGIN_DEFINE(
GST_VERSION_MAJOR,
GST_VERSION_MINOR,
- "polypsink",
- "Polypaudio Element Plugins",
+ "polypaudio",
+ "Polypaudio Elements Plugin",
plugin_init,
VERSION,
"LGPL",
diff --git a/src/polypmixer.c b/src/polypmixer.c
new file mode 100644
index 0000000..dfc8166
--- /dev/null
+++ b/src/polypmixer.c
@@ -0,0 +1,194 @@
+/* $Id$ */
+
+/***
+ This file is part of gst-polyp.
+
+ gst-polyp 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.1 of the
+ License, or (at your option) any later version.
+
+ gst-polyp 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 gst-polyp; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include "polypmixer.h"
+
+enum {
+ ARG_SERVER = 1,
+ ARG_DEVICE,
+ ARG_DEVICE_NAME
+};
+
+GST_DEBUG_CATEGORY_EXTERN(polyp_debug);
+#define GST_CAT_DEFAULT polyp_debug
+
+GST_IMPLEMENT_POLYPMIXER_CTRL_METHODS(GstPolypMixer, gst_polypmixer)
+GST_BOILERPLATE_WITH_INTERFACE(GstPolypMixer, gst_polypmixer, GstElement, GST_TYPE_ELEMENT, GstMixer, GST_TYPE_MIXER, gst_polypmixer)
+
+static void gst_polypmixer_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void gst_polypmixer_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void gst_polypmixer_finalize(GObject *object);
+static GstStateChangeReturn gst_polypmixer_change_state(GstElement *element, GstStateChange transition);
+
+static void gst_polypmixer_base_init(gpointer g_class) {
+
+ static const GstElementDetails details =
+ GST_ELEMENT_DETAILS(
+ "Polypaudio Mixer",
+ "Generic/Audio",
+ "Control sound input and output levels for Polypaudio",
+ "Lennart Poettering");
+
+ gst_element_class_set_details(GST_ELEMENT_CLASS(g_class), &details);
+}
+
+static void gst_polypmixer_class_init(GstPolypMixerClass *g_class) {
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS(g_class);
+ GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
+
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_polypmixer_change_state);
+
+ gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_polypmixer_finalize);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_polypmixer_get_property);
+ gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_polypmixer_set_property);
+
+ g_object_class_install_property(
+ gobject_class,
+ ARG_SERVER,
+ g_param_spec_string("server", "Server", "The Polypaudio server to connect to", NULL, G_PARAM_READWRITE));
+
+ g_object_class_install_property(
+ gobject_class,
+ ARG_DEVICE,
+ g_param_spec_string("device", "Sink/Source", "The Polypaudio sink or source to control", NULL, G_PARAM_READWRITE));
+
+ g_object_class_install_property(
+ gobject_class,
+ ARG_DEVICE_NAME,
+ g_param_spec_string("device-name", "Device name", "Human-readable name of the sound device", NULL, G_PARAM_READABLE));
+}
+
+static void gst_polypmixer_init(GstPolypMixer *this, GstPolypMixerClass *g_class) {
+ this->mixer = NULL;
+ this->server = NULL;
+ this->device = NULL;
+}
+
+static void gst_polypmixer_finalize(GObject *object) {
+ GstPolypMixer *this = GST_POLYPMIXER(object);
+
+ g_free(this->server);
+ g_free(this->device);
+
+ if (this->mixer) {
+ gst_polypmixer_ctrl_free(this->mixer);
+ this->mixer = NULL;
+ }
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void gst_polypmixer_set_property(
+ GObject * object,
+ guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec) {
+
+ GstPolypMixer *this = GST_POLYPMIXER(object);
+
+ switch (prop_id) {
+ case ARG_SERVER:
+ g_free(this->server);
+ this->server = g_value_dup_string(value);
+ break;
+
+ case ARG_DEVICE:
+ g_free(this->device);
+ this->device = g_value_dup_string(value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void gst_polypmixer_get_property(
+ GObject * object,
+ guint prop_id,
+ GValue * value,
+ GParamSpec * pspec) {
+
+ GstPolypMixer *this = GST_POLYPMIXER(object);
+
+ switch(prop_id) {
+
+ case ARG_SERVER:
+ g_value_set_string(value, this->server);
+ break;
+
+ case ARG_DEVICE:
+ g_value_set_string(value, this->device);
+ break;
+
+ case ARG_DEVICE_NAME:
+
+ if (this->mixer) {
+ char *t = g_strdup_printf("[%s] %s", this->mixer->name, this->mixer->description);
+ g_value_set_string(value, t);
+ g_free(t);
+ } else
+ g_value_set_string(value, NULL);
+
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstStateChangeReturn gst_polypmixer_change_state(GstElement *element, GstStateChange transition) {
+ GstPolypMixer *this = GST_POLYPMIXER(element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+
+ if (!this->mixer)
+ this->mixer = gst_polypmixer_ctrl_new(this->server, this->device);
+
+ break;
+
+ case GST_STATE_CHANGE_READY_TO_NULL:
+
+ if (this->mixer) {
+ gst_polypmixer_ctrl_free(this->mixer);
+ this->mixer = NULL;
+ }
+
+ break;
+
+ default:
+ ;
+ }
+
+ if (GST_ELEMENT_CLASS(parent_class)->change_state)
+ return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
+
+ return GST_STATE_CHANGE_SUCCESS;
+}
diff --git a/src/polypmixer.h b/src/polypmixer.h
new file mode 100644
index 0000000..63d0609
--- /dev/null
+++ b/src/polypmixer.h
@@ -0,0 +1,63 @@
+#ifndef __GST_POLYPMIXER_H__
+#define __GST_POLYPMIXER_H__
+
+/* $Id$ */
+
+/***
+ This file is part of gst-polyp.
+
+ gst-polyp 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.1 of the
+ License, or (at your option) any later version.
+
+ gst-polyp 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 gst-polyp; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <gst/gst.h>
+
+#include <polyp/polypaudio.h>
+#include <polyp/thread-mainloop.h>
+
+#include "polypmixerctrl.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_POLYPMIXER \
+ (gst_polypmixer_get_type())
+#define GST_POLYPMIXER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_POLYPMIXER,GstPolypMixer))
+#define GST_POLYPMIXER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_POLYPMIXER,GstPolypMixerClass))
+#define GST_IS_POLYPMIXER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_POLYPMIXER))
+#define GST_IS_POLYPMIXER_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_POLYPMIXER))
+
+typedef struct _GstPolypMixer GstPolypMixer;
+typedef struct _GstPolypMixerClass GstPolypMixerClass;
+
+struct _GstPolypMixer {
+ GstElement parent;
+
+ GstPolypMixerCtrl *mixer;
+ gchar *server, *device;
+};
+
+struct _GstPolypMixerClass {
+ GstElementClass parent_class;
+};
+
+GType gst_polypmixer_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GST_POLYPMIXER_H__ */
diff --git a/src/polypmixerctrl.c b/src/polypmixerctrl.c
new file mode 100644
index 0000000..122ade8
--- /dev/null
+++ b/src/polypmixerctrl.c
@@ -0,0 +1,458 @@
+/* $Id$ */
+
+/***
+ This file is part of gst-polyp.
+
+ gst-polyp 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.1 of the
+ License, or (at your option) any later version.
+
+ gst-polyp 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 gst-polyp; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+
+#include "polypmixerctrl.h"
+#include "polypmixertrack.h"
+#include "polyputil.h"
+
+GST_DEBUG_CATEGORY_EXTERN(polyp_debug);
+#define GST_CAT_DEFAULT polyp_debug
+
+static void gst_polypmixer_ctrl_context_state_cb(pa_context *context, void *userdata) {
+ GstPolypMixerCtrl *c = GST_POLYPMIXER_CTRL(userdata);
+
+ /* Called from the background thread! */
+
+ switch (pa_context_get_state(context)) {
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal(c->mainloop, 0);
+ break;
+
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+ }
+}
+
+static void gst_polypmixer_ctrl_sink_info_cb(pa_context *context, const pa_sink_info *i, int eol, void *userdata) {
+ GstPolypMixerCtrl *c = (GstPolypMixerCtrl*) userdata;
+
+ /* Called from the background thread! */
+
+ if (eol || !i)
+ return;
+
+ g_free(c->name);
+ g_free(c->description);
+ c->name = g_strdup(i->name);
+ c->description = g_strdup(i->description);
+ c->index = i->index;
+ c->channel_map = i->channel_map;
+ c->volume = i->volume;
+ c->muted = i->mute;
+ c->type = GST_POLYPMIXER_SINK;
+
+ c->operation_success = 1;
+
+ if (c->track)
+ c->track->flags = (c->track->flags & ~GST_MIXER_TRACK_MUTE) | (c->muted ? GST_MIXER_TRACK_MUTE : 0);
+
+ pa_threaded_mainloop_signal(c->mainloop, 0);
+}
+
+static void gst_polypmixer_ctrl_source_info_cb(pa_context *context, const pa_source_info *i, int eol, void *userdata) {
+ GstPolypMixerCtrl *c = (GstPolypMixerCtrl*) userdata;
+
+ /* Called from the background thread! */
+
+ if (eol || !i)
+ return;
+
+ g_free(c->name);
+ g_free(c->description);
+ c->name = g_strdup(i->description);
+ c->description = g_strdup(i->description);
+ c->index = i->index;
+ c->channel_map = i->channel_map;
+ c->volume = i->volume;
+ c->muted = i->mute;
+ c->type = GST_POLYPMIXER_SOURCE;
+
+ c->operation_success = 1;
+
+ if (c->track)
+ c->track->flags = (c->track->flags & ~GST_MIXER_TRACK_MUTE) | (c->muted ? GST_MIXER_TRACK_MUTE : 0);
+
+ pa_threaded_mainloop_signal(c->mainloop, 0);
+}
+
+static void gst_polypmixer_ctrl_subscribe_cb(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ GstPolypMixerCtrl *c = GST_POLYPMIXER_CTRL(userdata);
+ pa_operation *o = NULL;
+
+ /* Called from the background thread! */
+
+ if (c->index != idx)
+ return;
+
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
+ return;
+
+ if (c->type == GST_POLYPMIXER_SINK)
+ o = pa_context_get_sink_info_by_index(c->context, c->index, gst_polypmixer_ctrl_sink_info_cb, c);
+ else
+ o = pa_context_get_source_info_by_index(c->context, c->index, gst_polypmixer_ctrl_source_info_cb, c);
+
+ if (!o) {
+ GST_WARNING("Failed to get sink info: %s", pa_strerror(pa_context_errno(c->context)));
+ return;
+ }
+
+ pa_operation_unref(o);
+}
+
+static void gst_polypmixer_ctrl_success_cb(pa_context *context, int success, void *userdata) {
+ GstPolypMixerCtrl *c = (GstPolypMixerCtrl*) userdata;
+
+ c->operation_success = success;
+ pa_threaded_mainloop_signal(c->mainloop, 0);
+}
+
+#define CHECK_DEAD_GOTO(c, label) do { \
+if (!(c)->context || pa_context_get_state((c)->context) != PA_CONTEXT_READY) { \
+ GST_WARNING("Not connected: %s", (c)->context ? pa_strerror(pa_context_errno((c)->context)) : "NULL"); \
+ goto label; \
+} \
+} while(0);
+
+static gboolean gst_polypmixer_ctrl_open(GstPolypMixerCtrl *c) {
+ int e;
+ gchar *name = gst_polyp_client_name();
+ pa_operation *o = NULL;
+
+ g_assert(c);
+
+ c->mainloop = pa_threaded_mainloop_new();
+ g_assert(c->mainloop);
+
+ e = pa_threaded_mainloop_start(c->mainloop);
+ g_assert(e == 0);
+
+ pa_threaded_mainloop_lock(c->mainloop);
+
+ if (!(c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop), name))) {
+ GST_WARNING("Failed to create context");
+ goto unlock_and_fail;
+ }
+
+ pa_context_set_state_callback(c->context, gst_polypmixer_ctrl_context_state_cb, c);
+ pa_context_set_subscribe_callback(c->context, gst_polypmixer_ctrl_subscribe_cb, c);
+
+ if (pa_context_connect(c->context, c->server, 0, NULL) < 0) {
+ GST_WARNING("Failed to connect context: %s", pa_strerror(pa_context_errno(c->context)));
+ goto unlock_and_fail;
+ }
+
+ /* Wait until the context is ready */
+ pa_threaded_mainloop_wait(c->mainloop);
+
+ if (pa_context_get_state(c->context) != PA_CONTEXT_READY) {
+ GST_WARNING("Failed to connect context: %s", pa_strerror(pa_context_errno(c->context)));
+ goto unlock_and_fail;
+ }
+
+ /* Subscribe to events */
+
+ if (!(o = pa_context_subscribe(c->context, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_EVENT_SOURCE, gst_polypmixer_ctrl_success_cb, c))) {
+ GST_WARNING("Failed to subscribe to events: %s", pa_strerror(pa_context_errno(c->context)));
+ goto unlock_and_fail;
+ }
+
+ c->operation_success = 0;
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ pa_threaded_mainloop_wait(c->mainloop);
+ CHECK_DEAD_GOTO(c, unlock_and_fail);
+ }
+
+ if (!c->operation_success) {
+ GST_WARNING("Failed to subscribe to events: %s", pa_strerror(pa_context_errno(c->context)));
+ goto unlock_and_fail;
+ }
+
+ /* Get sink info */
+
+ if (!(o = pa_context_get_sink_info_by_name(c->context, c->device, gst_polypmixer_ctrl_sink_info_cb, c))) {
+ GST_WARNING("Failed to get sink info: %s", pa_strerror(pa_context_errno(c->context)));
+ goto unlock_and_fail;
+ }
+
+ c->operation_success = 0;
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ pa_threaded_mainloop_wait(c->mainloop);
+ CHECK_DEAD_GOTO(c, unlock_and_fail);
+ }
+
+ if (!c->operation_success) {
+
+ if (pa_context_errno(c->context) != PA_ERR_NOENTITY) {
+ GST_WARNING("Failed to get sink info: %s", pa_strerror(pa_context_errno(c->context)));
+ goto unlock_and_fail;
+ }
+
+ /* This device wasn't a sink, hence look for a source */
+
+ pa_operation_unref(o);
+
+ if (!(o = pa_context_get_source_info_by_name(c->context, c->device, gst_polypmixer_ctrl_source_info_cb, c))) {
+ GST_WARNING("Failed to get source info: %s", pa_strerror(pa_context_errno(c->context)));
+ goto unlock_and_fail;
+ }
+
+ c->operation_success = 0;
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ pa_threaded_mainloop_wait(c->mainloop);
+ CHECK_DEAD_GOTO(c, unlock_and_fail);
+ }
+
+ if (!c->operation_success) {
+ GST_WARNING("Failed to get source info: %s", pa_strerror(pa_context_errno(c->context)));
+ goto unlock_and_fail;
+ }
+ }
+
+ c->track = gst_polypmixer_track_new(c);
+ c->tracklist = g_list_append(c->tracklist, c->track);
+
+ pa_operation_unref(o);
+
+ pa_threaded_mainloop_unlock(c->mainloop);
+ g_free(name);
+
+ return TRUE;
+
+unlock_and_fail:
+
+ if (o)
+ pa_operation_unref(o);
+
+ if (c->mainloop)
+ pa_threaded_mainloop_unlock(c->mainloop);
+
+ g_free(name);
+
+ return FALSE;
+}
+
+static void gst_polypmixer_ctrl_close(GstPolypMixerCtrl *c) {
+ g_assert(c);
+
+ if (c->mainloop)
+ pa_threaded_mainloop_stop(c->mainloop);
+
+ if (c->context) {
+ pa_context_disconnect(c->context);
+ pa_context_unref(c->context);
+ c->context = NULL;
+ }
+
+ if (c->mainloop) {
+ pa_threaded_mainloop_free(c->mainloop);
+ c->mainloop = NULL;
+ c->time_event = NULL;
+ }
+
+ if (c->tracklist) {
+ g_list_free(c->tracklist);
+ c->tracklist = NULL;
+ }
+
+ if (c->track) {
+ GST_POLYPMIXER_TRACK(c->track)->control = NULL;
+ g_object_unref(c->track);
+ c->track = NULL;
+ }
+}
+
+GstPolypMixerCtrl* gst_polypmixer_ctrl_new(const gchar *server, const gchar *device) {
+ GstPolypMixerCtrl *c = NULL;
+
+ c = g_new(GstPolypMixerCtrl, 1);
+ c->tracklist = NULL;
+ c->server = g_strdup(server);
+ c->device = g_strdup(device);
+ c->mainloop = NULL;
+ c->context = NULL;
+ c->track = NULL;
+
+ pa_cvolume_mute(&c->volume, PA_CHANNELS_MAX);
+ pa_channel_map_init(&c->channel_map);
+ c->muted = 0;
+ c->index = PA_INVALID_INDEX;
+ c->type = GST_POLYPMIXER_UNKNOWN;
+ c->name = NULL;
+ c->description = NULL;
+
+ c->time_event = NULL;
+
+ if (!(gst_polypmixer_ctrl_open(c))) {
+ gst_polypmixer_ctrl_free(c);
+ return NULL;
+ }
+
+ return c;
+}
+
+void gst_polypmixer_ctrl_free(GstPolypMixerCtrl *c) {
+ g_assert(c);
+
+ gst_polypmixer_ctrl_close(c);
+
+ g_free(c->server);
+ g_free(c->device);
+ g_free(c->name);
+ g_free(c->description);
+ g_free(c);
+}
+
+const GList* gst_polypmixer_ctrl_list_tracks(GstPolypMixerCtrl *c) {
+ g_assert(c);
+
+ return c->tracklist;
+}
+
+static void gst_polypmixer_ctrl_timeout_event(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ pa_operation *o;
+ GstPolypMixerCtrl *c = GST_POLYPMIXER_CTRL(userdata);
+
+ if (c->type == GST_POLYPMIXER_SINK)
+ o = pa_context_set_sink_volume_by_index(c->context, c->index, &c->volume, NULL, NULL);
+ else
+ o = pa_context_set_source_volume_by_index(c->context, c->index, &c->volume, NULL, NULL);
+
+ if (!(o)) {
+ GST_WARNING("Failed to set device volume: %s", pa_strerror(pa_context_errno(c->context)));
+ else
+ pa_operation_unref(o);
+
+ g_assert(e == c->time_event);
+ a->time_free(e);
+ c->time_event = NULL;
+}
+
+static struct timeval* gst_polypmixer_ctrl_elapse(struct timeval *tv, unsigned msec) {
+ unsigned long secs;
+
+ gettimeofday(tv, NULL);
+
+ secs = (msec/1000);
+ tv->tv_sec += (unsigned long) secs;
+ msec -= secs*1000;
+
+ tv->tv_usec += msec*1000;
+
+ /* Normalize */
+ while (tv->tv_usec >= 1000000) {
+ tv->tv_sec++;
+ tv->tv_usec -= 1000000;
+ }
+
+ return tv;
+}
+
+void gst_polypmixer_ctrl_set_volume(GstPolypMixerCtrl *c, GstMixerTrack *track, gint *volumes) {
+ pa_cvolume v;
+ int i;
+
+ g_assert(c);
+ g_assert(track == c->track);
+
+ pa_threaded_mainloop_lock(c->mainloop);
+
+ for (i = 0; i < c->channel_map.channels; i++)
+ v.values[i] = (pa_volume_t) volumes[i];
+
+ v.channels = c->channel_map.channels;
+
+ c->volume = v;
+
+ if (!c->time_event) {
+ /* Updating the volume too often will cause a lot of traffic
+ * when accessing a networked server. Therefore we make sure
+ * to update the volume only once every 100ms */
+ struct timeval tv;
+ pa_mainloop_api *api = pa_threaded_mainloop_get_api(c->mainloop);
+ c->time_event = api->time_new(api, gst_polypmixer_ctrl_elapse(&tv, 100), gst_polypmixer_ctrl_timeout_event, c);
+ }
+
+ pa_threaded_mainloop_unlock(c->mainloop);
+}
+
+void gst_polypmixer_ctrl_get_volume(GstPolypMixerCtrl *c, GstMixerTrack *track, gint *volumes) {
+ int i;
+
+ g_assert(c);
+ g_assert(track == c->track);
+
+ pa_threaded_mainloop_lock(c->mainloop);
+
+ for (i = 0; i < c->channel_map.channels; i++)
+ volumes[i] = c->volume.values[i];
+
+ pa_threaded_mainloop_unlock(c->mainloop);
+}
+
+void gst_polypmixer_ctrl_set_record(GstPolypMixerCtrl *c, GstMixerTrack *track, gboolean record) {
+ g_assert(c);
+ g_assert(track == c->track);
+}
+
+void gst_polypmixer_ctrl_set_mute(GstPolypMixerCtrl *c, GstMixerTrack *track, gboolean mute) {
+ pa_operation *o = NULL;
+
+ g_assert(c);
+ g_assert(track == c->track);
+
+ pa_threaded_mainloop_lock(c->mainloop);
+
+ if (c->type == GST_POLYPMIXER_SINK)
+ o = pa_context_set_sink_mute_by_index(c->context, c->index, !!mute, NULL, NULL);
+ else
+ o = pa_context_set_source_mute_by_index(c->context, c->index, !!mute, NULL, NULL);
+
+ if (!(o)) {
+ GST_WARNING("Failed to set device mute status: %s", pa_strerror(pa_context_errno(c->context)));
+ goto unlock_and_fail;
+ }
+
+ c->muted = mute;
+
+ if (c->track)
+ c->track->flags = (c->track->flags & ~GST_MIXER_TRACK_MUTE) | (c->muted ? GST_MIXER_TRACK_MUTE : 0);
+
+ /* We're not interested in the return code of this operation */
+
+unlock_and_fail:
+
+ if (o)
+ pa_operation_unref(o);
+
+ pa_threaded_mainloop_unlock(c->mainloop);
+}
diff --git a/src/polypmixerctrl.h b/src/polypmixerctrl.h
new file mode 100644
index 0000000..18313c6
--- /dev/null
+++ b/src/polypmixerctrl.h
@@ -0,0 +1,148 @@
+#ifndef __GST_POLYPMIXERCTRL_H__
+#define __GST_POLYPMIXERCTRL_H__
+
+/* $Id$ */
+
+/***
+ This file is part of gst-polyp.
+
+ gst-polyp 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.1 of the
+ License, or (at your option) any later version.
+
+ gst-polyp 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 gst-polyp; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <gst/gst.h>
+#include <gst/interfaces/mixer.h>
+
+#include <polyp/polypaudio.h>
+#include <polyp/thread-mainloop.h>
+
+G_BEGIN_DECLS
+
+#define GST_POLYPMIXER_CTRL(obj) ((GstPolypMixerCtrl*)(obj))
+
+typedef struct _GstPolypMixerCtrl GstPolypMixerCtrl;
+
+struct _GstPolypMixerCtrl {
+ GList *tracklist;
+
+ gchar *server, *device;
+
+ pa_threaded_mainloop *mainloop;
+ pa_context *context;
+
+ gchar *name, *description;
+ pa_channel_map channel_map;
+ pa_cvolume volume;
+ int muted;
+ guint32 index;
+ enum { GST_POLYPMIXER_UNKNOWN, GST_POLYPMIXER_SINK, GST_POLYPMIXER_SOURCE } type;
+ int operation_success;
+
+ GstMixerTrack *track;
+
+ pa_time_event *time_event;
+};
+
+GstPolypMixerCtrl* gst_polypmixer_ctrl_new(const gchar *server, const gchar *device);
+void gst_polypmixer_ctrl_free(GstPolypMixerCtrl *mixer);
+
+const GList* gst_polypmixer_ctrl_list_tracks(GstPolypMixerCtrl *mixer);
+void gst_polypmixer_ctrl_set_volume(GstPolypMixerCtrl *mixer, GstMixerTrack *track, gint *volumes);
+void gst_polypmixer_ctrl_get_volume(GstPolypMixerCtrl *mixer, GstMixerTrack *track, gint *volumes);
+void gst_polypmixer_ctrl_set_mute(GstPolypMixerCtrl *mixer, GstMixerTrack *track, gboolean mute);
+void gst_polypmixer_ctrl_set_record(GstPolypMixerCtrl *mixer, GstMixerTrack *track, gboolean record);
+
+#define GST_IMPLEMENT_POLYPMIXER_CTRL_METHODS(Type, interface_as_function) \
+static gboolean \
+interface_as_function ## _supported (Type *this, GType iface_type) \
+{ \
+ g_assert (iface_type == GST_TYPE_MIXER); \
+ \
+ return (this->mixer != NULL); \
+} \
+ \
+static const GList* \
+interface_as_function ## _list_tracks (GstMixer * mixer) \
+{ \
+ Type *this = (Type*) mixer; \
+ \
+ g_return_val_if_fail (this != NULL, NULL); \
+ g_return_val_if_fail (this->mixer != NULL, NULL); \
+ \
+ return gst_polypmixer_ctrl_list_tracks (this->mixer); \
+} \
+static void \
+interface_as_function ## _set_volume (GstMixer * mixer, GstMixerTrack * track, \
+ gint * volumes) \
+{ \
+ Type *this = (Type*) mixer; \
+ \
+ g_return_if_fail (this != NULL); \
+ g_return_if_fail (this->mixer != NULL); \
+ \
+ gst_polypmixer_ctrl_set_volume (this->mixer, track, volumes); \
+} \
+static void \
+interface_as_function ## _get_volume (GstMixer * mixer, GstMixerTrack * track, \
+ gint * volumes) \
+{ \
+ Type *this = (Type*) mixer; \
+ \
+ g_return_if_fail (this != NULL); \
+ g_return_if_fail (this->mixer != NULL); \
+ \
+ gst_polypmixer_ctrl_get_volume (this->mixer, track, volumes); \
+} \
+ \
+static void \
+interface_as_function ## _set_record (GstMixer * mixer, GstMixerTrack * track, \
+ gboolean record) \
+{ \
+ Type *this = (Type*) mixer; \
+ \
+ g_return_if_fail (this != NULL); \
+ g_return_if_fail (this->mixer != NULL); \
+ \
+ gst_polypmixer_ctrl_set_record (this->mixer, track, record); \
+} \
+ \
+static void \
+interface_as_function ## _set_mute (GstMixer * mixer, GstMixerTrack * track, \
+ gboolean mute) \
+{ \
+ Type *this = (Type*) mixer; \
+ \
+ g_return_if_fail (this != NULL); \
+ g_return_if_fail (this->mixer != NULL); \
+ \
+ gst_polypmixer_ctrl_set_mute (this->mixer, track, mute); \
+} \
+ \
+static void \
+interface_as_function ## _interface_init (GstMixerClass * klass) \
+{ \
+ GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE; \
+ \
+ /* set up the interface hooks */ \
+ klass->list_tracks = interface_as_function ## _list_tracks; \
+ klass->set_volume = interface_as_function ## _set_volume; \
+ klass->get_volume = interface_as_function ## _get_volume; \
+ klass->set_mute = interface_as_function ## _set_mute; \
+ klass->set_record = interface_as_function ## _set_record; \
+}
+
+G_END_DECLS
+
+#endif
diff --git a/src/polypmixertrack.c b/src/polypmixertrack.c
new file mode 100644
index 0000000..75101bd
--- /dev/null
+++ b/src/polypmixertrack.c
@@ -0,0 +1,59 @@
+/* $Id$ */
+
+/***
+ This file is part of gst-polyp.
+
+ gst-polyp 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.1 of the
+ License, or (at your option) any later version.
+
+ gst-polyp 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 gst-polyp; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+
+#include "polypmixertrack.h"
+
+GST_DEBUG_CATEGORY_EXTERN(polyp_debug);
+#define GST_CAT_DEFAULT polyp_debug
+
+G_DEFINE_TYPE(GstPolypMixerTrack, gst_polypmixer_track, GST_TYPE_MIXER_TRACK);
+
+static void gst_polypmixer_track_class_init(GstPolypMixerTrackClass *klass) {
+}
+
+static void gst_polypmixer_track_init(GstPolypMixerTrack *track) {
+ track->control = NULL;
+}
+
+GstMixerTrack *gst_polypmixer_track_new(GstPolypMixerCtrl *control) {
+ GstPolypMixerTrack *polyptrack;
+ GstMixerTrack *track;
+
+ polyptrack = g_object_new(GST_TYPE_POLYPMIXER_TRACK, NULL);
+ polyptrack->control = control;
+
+ track = GST_MIXER_TRACK(polyptrack);
+ track->label = g_strdup("Master");
+ track->num_channels = control->channel_map.channels;
+ track->flags =
+ (control->type == GST_POLYPMIXER_SINK ? GST_MIXER_TRACK_OUTPUT|GST_MIXER_TRACK_MASTER : GST_MIXER_TRACK_INPUT|GST_MIXER_TRACK_RECORD) |
+ (control->muted ? GST_MIXER_TRACK_MUTE : 0);
+ track->min_volume = PA_VOLUME_MUTED;
+ track->max_volume = PA_VOLUME_NORM;
+
+ return track;
+}
diff --git a/src/polypmixertrack.h b/src/polypmixertrack.h
new file mode 100644
index 0000000..b7cc901
--- /dev/null
+++ b/src/polypmixertrack.h
@@ -0,0 +1,56 @@
+#ifndef __GST_POLYPMIXERTRACK_H__
+#define __GST_POLYPMIXERTRACK_H__
+
+/* $Id$ */
+
+/***
+ This file is part of gst-polyp.
+
+ gst-polyp 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.1 of the
+ License, or (at your option) any later version.
+
+ gst-polyp 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 gst-polyp; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <gst/gst.h>
+
+#include "polypmixerctrl.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_POLYPMIXER_TRACK \
+ (gst_polypmixer_track_get_type())
+#define GST_POLYPMIXER_TRACK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_POLYPMIXER_TRACK, GstPolypMixerTrack))
+#define GST_POLYPMIXER_TRACK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_POLYPMIXER_TRACK, GstPolypMixerTrackClass))
+#define GST_IS_POLYPMIXER_TRACK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_POLYPMIXER_TRACK))
+#define GST_IS_POLYPMIXER_TRACK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_POLYPMIXER_TRACK))
+
+typedef struct _GstPolypMixerTrack {
+ GstMixerTrack parent;
+ GstPolypMixerCtrl *control;
+} GstPolypMixerTrack;
+
+typedef struct _GstPolypMixerTrackClass {
+ GstMixerTrackClass parent;
+} GstPolypMixerTrackClass;
+
+GType gst_polypmixer_track_get_type(void);
+GstMixerTrack* gst_polypmixer_track_new(GstPolypMixerCtrl *control);
+
+G_END_DECLS
+
+#endif
diff --git a/src/polypsink.c b/src/polypsink.c
index e154e37..f64685d 100644
--- a/src/polypsink.c
+++ b/src/polypsink.c
@@ -36,8 +36,7 @@ GST_DEBUG_CATEGORY_EXTERN(polyp_debug);
#define GST_CAT_DEFAULT polyp_debug
enum {
- ARG_0,
- ARG_SERVER,
+ ARG_SERVER = 1,
ARG_SINK,
};
@@ -109,7 +108,7 @@ static void gst_polypsink_base_init(gpointer g_class) {
static const GstElementDetails details =
GST_ELEMENT_DETAILS(
- "Polypaudio audio sink",
+ "Polypaudio Audio Sink",
"Sink/Audio",
"Plays audio to a Polypaudio server",
"Lennart Poettering");
diff --git a/src/polypsrc.c b/src/polypsrc.c
index a790e6d..00d5657 100644
--- a/src/polypsrc.c
+++ b/src/polypsrc.c
@@ -106,7 +106,7 @@ static void gst_polypsrc_base_init(gpointer g_class) {
static const GstElementDetails details =
GST_ELEMENT_DETAILS(
- "Polypaudio audio source",
+ "Polypaudio Audio Source",
"Source/Audio",
"Captures audio from a Polypaudio server",
"Lennart Poettering");