summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2006-05-20 00:55:56 +0000
committerLennart Poettering <lennart@poettering.net>2006-05-20 00:55:56 +0000
commitdab1e2dafa8628fead93cb3590b6c5ff07994e9e (patch)
tree79d5a65f89ab70e2909342ec5a5691520c18e171
parentce0c65a2989ef19de6be2e1e96c3d8a5f892d5d3 (diff)
update for polypaudio 0.9
git-svn-id: file:///home/lennart/svn/public/xmms-pulse/trunk@36 ef929aba-56e2-0310-84e0-b7573d389508
-rwxr-xr-xbootstrap.sh41
-rw-r--r--configure.ac14
-rw-r--r--src/Makefile.am10
-rw-r--r--src/plugin.c998
4 files changed, 599 insertions, 464 deletions
diff --git a/bootstrap.sh b/bootstrap.sh
index dd5d53b..597fad8 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -17,31 +17,48 @@
# along with xmms-polyp; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+VERSION=1.9
+
run_versioned() {
local P
- type -p "$1-$2" &> /dev/null && P="$1-$2" || local P="$1"
+ local V
+
+ V=$(echo "$2" | sed -e 's,\.,,g')
+
+ if [ -e "`which $1$V`" ] ; then
+ P="$1$V"
+ else
+ if [ -e "`which $1-$2`" ] ; then
+ P="$1-$2"
+ else
+ P="$1"
+ fi
+ fi
shift 2
"$P" "$@"
}
+set -ex
+
if [ "x$1" = "xam" ] ; then
- set -ex
- run_versioned automake 1.7 -a -c --foreign
+ run_versioned automake "$VERSION" -a -c --foreign
./config.status
else
- set -ex
-
rm -rf autom4te.cache
rm -f config.cache
- run_versioned aclocal 1.7
- libtoolize -c --force
- autoheader
- run_versioned automake 1.7 -a -c --foreign
- autoconf -Wall
+ test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize
- CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@"
+ "$LIBTOOLIZE" -c --force
+ run_versioned aclocal "$VERSION"
+ libtoolize -c --force
+ run_versioned autoconf 2.59 -Wall
+ run_versioned autoheader 2.59
+ run_versioned automake "$VERSION" -a -c --foreign
- make clean
+ if test "x$NOCONFIGURE" = "x"; then
+ CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@"
+ make clean
+ fi
fi
diff --git a/configure.ac b/configure.ac
index abf3727..0be433d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,10 +20,10 @@
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
AC_PREREQ(2.57)
-AC_INIT([xmms-polyp],[0.5],[mzkzzfcbylc (at) 0pointer (dot) de])
+AC_INIT([xmms-polyp],[0.9.0],[mzkzzfcbylc (at) 0pointer (dot) de])
AC_CONFIG_SRCDIR([src/plugin.c])
AC_CONFIG_HEADERS([config.h])
-AM_INIT_AUTOMAKE([foreign -Wall])
+AM_INIT_AUTOMAKE([foreign 1.9 -Wall])
AC_SUBST(PACKAGE_URL, [http://0pointer.de/lennart/projects/xmms-polyp/])
@@ -43,9 +43,15 @@ AC_C_CONST
AC_FUNC_MALLOC
AC_TYPE_SIZE_T
-ACX_PTHREAD
+PKG_PROG_PKG_CONFIG
-PKG_CHECK_MODULES(POLYP, [ polyplib >= 0.8 ])
+if test -d ../polypaudio ; then
+ POLYP_CFLAGS='-I$(top_srcdir)/../polypaudio/src'
+ POLYP_LIBS='-L$(top_srcdir)/../polypaudio/src/.libs -lpolyp-simple'
+ echo "*** Found polypaudio in ../polypaudio, using that version ***"
+else
+ PKG_CHECK_MODULES(POLYP, [ polyplib-simple >= 0.9.0 ])
+fi
AC_SUBST(POLYP_LIBS)
AC_SUBST(POLYP_CFLAGS)
diff --git a/src/Makefile.am b/src/Makefile.am
index 7ff61a4..192b72d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,9 +22,9 @@ AM_LIBADD=$(PTHREAD_LIBS)
xmmsplugindir=$(XMMS_OUTPUTPLUGINDIR)
-xmmsplugin_LTLIBRARIES=libpolyp.la
+xmmsplugin_LTLIBRARIES=libxmms-polyp.la
-libpolyp_la_SOURCES=plugin.c
-libpolyp_la_LDFLAGS=-module -avoid-version
-libpolyp_la_LIBADD=$(AM_LIBADD) $(POLYP_LIBS) $(XMMS_LIBS)
-libpolyp_la_CFLAGS=$(AM_CFLAGS) $(POLYP_CFLAGS) $(XMMS_CFLAGS)
+libxmms_polyp_la_SOURCES=plugin.c
+libxmms_polyp_la_LDFLAGS=-module -avoid-version
+libxmms_polyp_la_LIBADD=$(AM_LIBADD) $(POLYP_LIBS) $(XMMS_LIBS)
+libxmms_polyp_la_CFLAGS=$(AM_CFLAGS) $(POLYP_CFLAGS) $(XMMS_CFLAGS)
diff --git a/src/plugin.c b/src/plugin.c
index 95ed252..47b93c7 100644
--- a/src/plugin.c
+++ b/src/plugin.c
@@ -31,64 +31,42 @@
#include <plugin.h>
#include <xmmsctrl.h>
#include <util.h>
-#include <polyp/mainloop.h>
+
#include <polyp/polypaudio.h>
-static pthread_cond_t request_cond = PTHREAD_COND_INITIALIZER;
-static pthread_mutex_t request_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-struct request {
- enum {
- MESSAGE_OPEN,
- MESSAGE_CLOSE,
- MESSAGE_WRITE,
- MESSAGE_FLUSH,
- MESSAGE_PAUSE,
- MESSAGE_UNPAUSE,
- MESSAGE_LATENCY,
- MESSAGE_WRITABLE,
- MESSAGE_TRIGGER,
- MESSAGE_GETVOLUME,
- MESSAGE_SETVOLUME
- } message;
- void *data;
- struct pa_sample_spec ss;
- size_t length;
- int success, done;
- uint32_t value;
- pa_usec_t latency;
- pa_cvolume volume;
-};
-
-static struct request* current_request = NULL;
-
-static int pipe_fds[2] = { -1, -1};
-
-static pthread_t thread_id;
-static int thread_running = 0;
-static struct pa_context *context = NULL;
-static struct pa_stream *stream = NULL;
-static struct pa_mainloop_api *mainloop_api = NULL;
-static int failed = 0;
+static pa_context *context = NULL;
+static pa_stream *stream = NULL;
+static pa_threaded_mainloop *mainloop = NULL;
+
static pa_cvolume volume;
-static size_t written = 0;
-static int do_trigger = 0, triggered = 0;
-static struct pa_sample_spec sample_spec;
-static int locked_by_thread = 0;
-static pa_cvolume saved_volume;
+static int volume_valid = 0;
-/* This function is from xmms' core */
-gint ctrlsocket_get_session_id(void);
+static int do_trigger = 0;
+static uint64_t written = 0;
+static int time_offset_msec = 0;
+static int just_flushed = 0;
-static void *memdup(void *p, size_t l) {
- void *r = malloc(l);
- memcpy(r, p, l);
- return r;
-}
+static int connected = 0;
+
+static pa_time_event *volume_time_event = NULL;
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX 255
-#endif
+#define CHECK_DEAD_GOTO(label, warn) do { \
+if (!mainloop || \
+ !context || pa_context_get_state(context) != PA_CONTEXT_READY || \
+ !stream || pa_stream_get_state(stream) != PA_STREAM_READY) { \
+ if (warn) \
+ g_warning("Connection died: %s", context ? pa_strerror(pa_context_errno(context)) : "NULL"); \
+ goto label; \
+ } \
+} while(0);
+
+#define CHECK_CONNECTED(retval) \
+do { \
+ if (!connected) return retval; \
+} while (0);
+
+/* This function is from xmms' core */
+gint ctrlsocket_get_session_id(void);
static const char* get_song_name(void) {
static char t[256];
@@ -98,489 +76,616 @@ static const char* get_song_name(void) {
session = ctrlsocket_get_session_id();
pos = xmms_remote_get_playlist_pos(session);
if (!(str = xmms_remote_get_playlist_title(session, pos)))
- return "xmms";
+ return "Playback Stream";
- snprintf(t, sizeof(t), "XMMS [%s]", str);
+ snprintf(t, sizeof(t), "%s", str);
return t;
}
-static const char* get_host(void) {
- static char t[HOST_NAME_MAX+1];
- gethostname(t, sizeof(t));
- t[HOST_NAME_MAX] = 0;
- return t;
-}
-
-static void lock_request(void) {
- assert(thread_running && pthread_equal(pthread_self(), thread_id));
-
- if (!(locked_by_thread++))
- pthread_mutex_lock(&request_mutex);
-}
-
-static void unlock_request(void) {
- assert(thread_running && pthread_equal(pthread_self(), thread_id) && locked_by_thread > 0);
-
- if (!(--locked_by_thread))
- pthread_mutex_unlock(&request_mutex);
-}
-
-static void finish_request(int success) {
- if (!success)
- failed = 1;
-
- lock_request();
-
- if (current_request) {
- current_request->done = 1;
- current_request->success = success;
- pthread_cond_broadcast(&request_cond);
- }
-
- unlock_request();
-}
-
-static void info_callback(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
- assert(c && c == context);
+static void info_cb(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
+ assert(c);
if (!i)
return;
volume = i->volume;
+ volume_valid = 1;
}
-static void subscribe_callback(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) {
- assert(c && c == context);
+static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) {
+ pa_operation *o;
+
+ assert(c);
- if (!stream || index != pa_stream_get_index(stream) || t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
+ if (!stream ||
+ index != pa_stream_get_index(stream) ||
+ (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+ t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW)))
return;
- pa_operation_unref(pa_context_get_sink_input_info(c, index, info_callback, NULL));
-}
-
-static void stream_state_callback(struct pa_stream *s, void *userdata) {
- assert(stream == s);
-
- switch(pa_stream_get_state(s)) {
- case PA_STREAM_CREATING:
- break;
- case PA_STREAM_READY:
- assert(current_request && current_request->message == MESSAGE_OPEN);
- pa_operation_unref(pa_context_get_sink_input_info(context, pa_stream_get_index(s), info_callback, NULL));
- finish_request(1);
- break;
- default:
- finish_request(0);
+ if (!(o = pa_context_get_sink_input_info(c, index, info_cb, NULL))) {
+ g_warning("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(c)));
+ return;
}
+
+ pa_operation_unref(o);
}
-static void context_state_callback(struct pa_context *c, void *userdata) {
- assert(c && c == context);
+static void context_state_cb(pa_context *c, void *userdata) {
+ assert(c);
switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal(mainloop, 0);
+ break;
+
+ case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
+ }
+}
- case PA_CONTEXT_READY :
- assert(!stream && current_request);
-
- pa_context_set_subscribe_callback(context, subscribe_callback, NULL);
- pa_operation_unref(pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL));
+static void stream_state_cb(pa_stream *s, void * userdata) {
+ assert(s);
- stream = pa_stream_new(c, get_song_name(), &current_request->ss, NULL);
- assert(stream);
+ switch (pa_stream_get_state(s)) {
- pa_stream_set_state_callback(stream, stream_state_callback, NULL);
- pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_INTERPOLATE_LATENCY, &current_request->volume, NULL);
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal(mainloop, 0);
break;
- default:
- finish_request(0);
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ break;
}
}
-static void context_drain_callback(struct pa_context *c, void *userdata) {
- assert(c && c == context);
- mainloop_api->quit(mainloop_api, 0);
- finish_request(1);
+static void stream_success_cb(pa_stream *s, int success, void *userdata) {
+ assert(s);
+
+ if (userdata)
+ *(int*) userdata = success;
+
+ pa_threaded_mainloop_signal(mainloop, 0);
}
-static void stream_success_callback(struct pa_stream *s, int success, void *userdata) {
- assert(s == stream && s);
- finish_request(!!success);
+static void context_success_cb(pa_context *c, int success, void *userdata) {
+ assert(c);
+
+ if (userdata)
+ *(int*) userdata = success;
+
+ pa_threaded_mainloop_signal(mainloop, 0);
}
-static void context_success_callback(struct pa_context *c, int success, void *userdata) {
- assert(c == context && c);
- finish_request(!!success);
+static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
+ assert(s);
+
+ pa_threaded_mainloop_signal(mainloop, 0);
}
-static void request_func(struct pa_mainloop_api*api, struct pa_io_event *io, int fd, enum pa_io_event_flags f, void *userdata) {
- char x;
-
- /*fprintf(stderr, "REQUEST %p\n", current_request);*/
-
- assert(api && io && f == PA_IO_EVENT_INPUT);
-
- read(fd, &x, 1);
-
- lock_request();
-
- if (current_request) {
- if (failed && current_request->message != MESSAGE_CLOSE) {
- finish_request(0);
- } else {
-
- /*fprintf(stderr, "req: %i\n", current_request->message);*/
-
- switch (current_request->message) {
- case MESSAGE_OPEN: {
- char t[64];
- assert(!context && !stream);
-
- snprintf(t, sizeof(t), "XMMS (PID %lu on %s)", (unsigned long) getpid(), get_host());
- context = pa_context_new(api, t);
- assert(context);
- pa_context_set_state_callback(context, context_state_callback, NULL);
- pa_context_connect(context, NULL, 1, NULL);
- break;
-
- }
- case MESSAGE_CLOSE: {
- struct pa_operation *o;
-
- assert(context);
- if (stream)
- pa_stream_disconnect(stream);
- if ((o = pa_context_drain(context, context_drain_callback, NULL)))
- pa_operation_unref(o);
- else {
- api->quit(mainloop_api, 0);
- finish_request(1);
- }
- break;
- }
-
- case MESSAGE_WRITE:
- assert(context && stream && current_request->data && current_request->length > 0);
- pa_stream_write(stream, current_request->data, current_request->length, free, 0, 0);
- current_request->data = NULL;
- finish_request(1);
- break;
-
- case MESSAGE_FLUSH:
- assert(context && stream);
- pa_operation_unref(pa_stream_flush(stream, stream_success_callback, NULL));
- break;
-
- case MESSAGE_PAUSE:
- case MESSAGE_UNPAUSE:
- assert(context && stream);
- pa_operation_unref(pa_stream_cork(stream, current_request->message == MESSAGE_PAUSE, stream_success_callback, NULL));
- break;
-
- case MESSAGE_LATENCY:
- current_request->latency = context && stream ? pa_stream_get_interpolated_latency(stream, NULL) : 0;
- finish_request(1);
- break;
-
- case MESSAGE_WRITABLE:
- assert(context && stream);
- current_request->value = pa_stream_writable_size(stream);
- finish_request(1);
- break;
-
- case MESSAGE_GETVOLUME:
- assert(context && stream);
- current_request->volume = volume;
- finish_request(1);
- break;
-
- case MESSAGE_SETVOLUME:
- assert(context && stream);
-
- if (pa_cvolume_equal(&current_request->volume, &volume))
- finish_request(1);
- else
- pa_operation_unref(pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &current_request->volume, context_success_callback, NULL));
- break;
-
- case MESSAGE_TRIGGER:
- assert(context && stream);
- pa_operation_unref(pa_stream_trigger(stream, stream_success_callback, NULL));
- break;
-
- }
- }
- }
-
- unlock_request();
+static void stream_latency_update_cb(pa_stream *s, void *userdata) {
+ assert(s);
- /*fprintf(stderr, "REQ_DONE\n");*/
+ pa_threaded_mainloop_signal(mainloop, 0);
}
-static void* thread_func(void *t) {
- struct pa_mainloop *m;
- struct pa_io_event *io;
+static void polyp_get_volume(int *l, int *r) {
+ pa_cvolume v;
+ int b = 0;
- assert(pipe_fds[0] >= 0 && !mainloop_api);
-
- failed = locked_by_thread = 0;
- thread_running = 1;
+/* g_message("get_volume"); */
+
+ *l = *r = 100;
+
+ if (connected) {
+ pa_threaded_mainloop_lock(mainloop);
+ CHECK_DEAD_GOTO(fail, 1);
+
+ v = volume;
+ b = volume_valid;
+
+ fail:
+ pa_threaded_mainloop_unlock(mainloop);
+ } else {
+ v = volume;
+ b = volume_valid;
+ }
- m = pa_mainloop_new();
- assert(m);
- mainloop_api = pa_mainloop_get_api(m);
- assert(mainloop_api);
+ if (b) {
+ if (v.channels == 2) {
+ *l = (int) ((v.values[0]*100)/PA_VOLUME_NORM);
+ *r = (int) ((v.values[1]*100)/PA_VOLUME_NORM);
+ } else
+ *l = *r = (int) ((pa_cvolume_avg(&v)*100)/PA_VOLUME_NORM);
+ }
+}
- io = mainloop_api->io_new(mainloop_api, pipe_fds[0], PA_IO_EVENT_INPUT, &request_func, NULL);
- assert(io);
+static void volume_time_cb(pa_mainloop_api *api, pa_time_event *e, const struct timeval *tv, void *userdata) {
+ pa_operation *o;
- pa_mainloop_run(m, NULL);
+ if (!(o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL)))
+ g_warning("pa_context_set_sink_input_volume() failed: %s", pa_strerror(pa_context_errno(context)));
+ else
+ pa_operation_unref(o);
- if (context) {
- pa_context_unref(context);
- context = NULL;
+ /* We don't wait for completion of this command */
+
+ api->time_free(volume_time_event);
+ volume_time_event = NULL;
+}
+
+static void polyp_set_volume(int l, int r) {
+
+/* g_message("set_volume"); */
+
+ if (connected) {
+ pa_threaded_mainloop_lock(mainloop);
+ CHECK_DEAD_GOTO(fail, 1);
}
- if (stream) {
- pa_stream_unref(stream);
- stream = NULL;
+ if (!volume_valid || volume.channels != 1) {
+ volume.values[0] = ((pa_volume_t) l * PA_VOLUME_NORM)/100;
+ volume.values[1] = ((pa_volume_t) r * PA_VOLUME_NORM)/100;
+ volume.channels = 2;
+ } else {
+ volume.values[0] = ((pa_volume_t) l * PA_VOLUME_NORM)/100;
+ volume.channels = 1;
}
- mainloop_api->io_free(io);
- pa_mainloop_free(m);
+ volume_valid = 1;
- mainloop_api = NULL;
- thread_running = 0;
+ if (connected && !volume_time_event) {
+ struct timeval tv;
+ pa_mainloop_api *api = pa_threaded_mainloop_get_api(mainloop);
+ volume_time_event = api->time_new(api, pa_timeval_add(pa_gettimeofday(&tv), 100000), volume_time_cb, NULL);
+ }
- pthread_cond_broadcast(&request_cond);
-
- return NULL;
+fail:
+ if (connected)
+ pa_threaded_mainloop_unlock(mainloop);
}
-static void start_thread(void) {
- int r;
- assert(!thread_running);
+static void polyp_pause(short b) {
+ pa_operation *o = NULL;
+ int success = 0;
- r = pipe(pipe_fds);
- assert(r >= 0 && pipe_fds[0] >= 0 && pipe_fds[1] >= 0);
+/* g_message("pause"); */
- current_request = NULL;
+ CHECK_CONNECTED();
- r = pthread_create(&thread_id, NULL, thread_func, NULL);
- assert(!r);
+ pa_threaded_mainloop_lock(mainloop);
+ CHECK_DEAD_GOTO(fail, 1);
- thread_running = 1;
-}
+ if (!(o = pa_stream_cork(stream, b, stream_success_cb, &success))) {
+ g_warning("pa_stream_cork() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto fail;
+ }
+
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ CHECK_DEAD_GOTO(fail, 1);
+ pa_threaded_mainloop_wait(mainloop);
+ }
-static void stop_thread(void) {
- pthread_join(thread_id, NULL);
- thread_running = 0;
- close(pipe_fds[0]);
- close(pipe_fds[1]);
- pipe_fds[0] = pipe_fds[1] = -1;
-}
+ if (!success)
+ g_warning("pa_stream_cork() failed: %s", pa_strerror(pa_context_errno(context)));
-static void execute_request(struct request *r) {
- char x = 'x';
- assert(r);
+fail:
- r->success = r->done = 0;
+ if (o)
+ pa_operation_unref(o);
- pthread_mutex_lock(&request_mutex);
+ pa_threaded_mainloop_unlock(mainloop);
+}
- while (current_request && thread_running)
- pthread_cond_wait(&request_cond, &request_mutex);
-
- if (!thread_running) {
- r->success = 0;
- r->done = 1;
- } else {
- current_request = r;
-
- assert(pipe_fds[1] >= 0);
- write(pipe_fds[1], &x, sizeof(x));
+static int polyp_free(void) {
+ size_t l = 0;
+ pa_operation *o = NULL;
- while (!r->done && thread_running)
- pthread_cond_wait(&request_cond, &request_mutex);
+/* g_message("free"); */
- if (!thread_running) {
- r->success = 0;
- r->done = 1;
- }
+ CHECK_CONNECTED(0);
- current_request = NULL;
+ pa_threaded_mainloop_lock(mainloop);
+ CHECK_DEAD_GOTO(fail, 1);
- /* Notify other waiting threads that the request was completed */
- pthread_cond_broadcast(&request_cond);
+ if ((l = pa_stream_writable_size(stream)) == (size_t) -1) {
+ g_warning("pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(context)));
+ l = 0;
+ goto fail;
+ }
+
+ /* If this function is called twice with no polyp_write() call in
+ * between this means we should trigger the playback */
+ if (do_trigger) {
+ int success = 0;
+
+ if (!(o = pa_stream_trigger(stream, stream_success_cb, &success))) {
+ g_warning("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto fail;
+ }
+
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ CHECK_DEAD_GOTO(fail, 1);
+ pa_threaded_mainloop_wait(mainloop);
+ }
+
+ if (!success)
+ g_warning("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
}
- pthread_mutex_unlock(&request_mutex);
+fail:
+ pa_threaded_mainloop_unlock(mainloop);
+
+ do_trigger = !!l;
+ return (int) l;
}
-static void polyp_get_volume(int *l, int *r) {
- struct request req;
- pa_cvolume v;
+static int polyp_get_written_time(void) {
+ int r = 0;
+
+/* g_message("get_written_time"); */
- req.message = MESSAGE_GETVOLUME;
- execute_request(&req);
+ CHECK_CONNECTED(0);
- if (!req.success)
- v = saved_volume;
- else
- saved_volume = v = req.volume;
+ pa_threaded_mainloop_lock(mainloop);
+ CHECK_DEAD_GOTO(fail, 1);
+
+ r = (int) (((double) written*1000) / pa_bytes_per_second(pa_stream_get_sample_spec(stream)));
- *l = (int) (pa_sw_volume_to_linear(v.values[0]) * 100);
- if (sample_spec.channels > 1)
- *r = (int) (pa_sw_volume_to_linear(v.values[1]) * 100);
- else
- *r = *l;
+/* g_message("written_time = %i", r); */
+
+fail:
+ pa_threaded_mainloop_unlock(mainloop);
+
+ return r;
}
-void polyp_set_volume(int l, int r) {
- struct request req;
+static int polyp_get_output_time(void) {
+ int r = 0;
+ pa_usec_t t;
+
+/* g_message("get_output_time"); */
+
+ CHECK_CONNECTED(0);
- req.message = MESSAGE_SETVOLUME;
- pa_cvolume_reset(&req.volume, sample_spec.channels);
- req.volume.values[0] = pa_sw_volume_from_linear(l/100.0);
- if (sample_spec.channels > 1)
- req.volume.values[1] = pa_sw_volume_from_linear(r/100.0);
- execute_request(&req);
+ pa_threaded_mainloop_lock(mainloop);
- saved_volume = req.volume;
-}
+ for (;;) {
+ CHECK_DEAD_GOTO(fail, 1);
+
+ if (pa_stream_get_time(stream, &t) >= 0)
+ break;
-static void polyp_pause(short b) {
- struct request r;
- r.message = b ? MESSAGE_PAUSE : MESSAGE_UNPAUSE;
- execute_request(&r);
-}
+ if (pa_context_errno(context) != PA_ERR_NODATA) {
+ g_warning("pa_stream_get_time() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto fail;
+ }
-static int polyp_free(void) {
- int ret;
- struct request r;
+ pa_threaded_mainloop_wait(mainloop);
+ }
- r.message = MESSAGE_WRITABLE;
- execute_request(&r);
+ r = (int) (t / 1000);
- if (!r.success)
- return 0;
+ if (just_flushed) {
+ time_offset_msec -= r;
+ just_flushed = 0;
+ }
- ret = r.value;
+ r += time_offset_msec;
+
+/* g_message("output_time = %i", r); */
- if (do_trigger && !triggered) {
- r.message = MESSAGE_TRIGGER;
- execute_request(&r);
- triggered = 1;
- }
+fail:
+ pa_threaded_mainloop_unlock(mainloop);
- do_trigger = !!ret;
- return ret;
+ return r;
}
-static int polyp_get_written_time(void) {
- return (int) ((((double) written/pa_frame_size(&sample_spec))*1000)/sample_spec.rate);
-}
+static int polyp_playing(void) {
+ int r = 0;
+ const pa_timing_info *i;
-static int polyp_get_output_time(void) {
- int t, ms;
- struct request r;
+ CHECK_CONNECTED(0);
+
+/* g_message("playing"); */
- r.message = MESSAGE_LATENCY;
- execute_request(&r);
+ pa_threaded_mainloop_lock(mainloop);
- if (!r.success)
- return 0;
+ for (;;) {
+ CHECK_DEAD_GOTO(fail, 1);
- t = polyp_get_written_time();
- ms = (int) r.latency/1000;
-
- return ms > t ? 0 : t-ms;
-}
+ if ((i = pa_stream_get_timing_info(stream)))
+ break;
+
+ if (pa_context_errno(context) != PA_ERR_NODATA) {
+ g_warning("pa_stream_get_timing_info() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto fail;
+ }
-static int polyp_playing(void) {
+ pa_threaded_mainloop_wait(mainloop);
+ }
+
+ r = i->playing;
+fail:
+ pa_threaded_mainloop_unlock(mainloop);
- return polyp_get_output_time() >= polyp_get_written_time();
+ return r;
}
static void polyp_flush(int time) {
- struct request r;
+ pa_operation *o = NULL;
+ int success = 0;
+
+/* g_message("flush"); */
+
+ CHECK_CONNECTED();
- r.message = MESSAGE_FLUSH;
- execute_request(&r);
+ pa_threaded_mainloop_lock(mainloop);
+ CHECK_DEAD_GOTO(fail, 1);
- triggered = 0;
+ if (!(o = pa_stream_flush(stream, stream_success_cb, &success))) {
+ g_warning("pa_stream_flush() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto fail;
+ }
+
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ CHECK_DEAD_GOTO(fail, 1);
+ pa_threaded_mainloop_wait(mainloop);
+ }
- written = (size_t) (((double)time*sample_spec.rate/1000)*pa_frame_size(&sample_spec));
+ if (!success)
+ g_warning("pa_stream_flush() failed: %s", pa_strerror(pa_context_errno(context)));
+
+ written = (uint64_t) (((double) time * pa_bytes_per_second(pa_stream_get_sample_spec(stream))) / 1000);
+ just_flushed = 1;
+ time_offset_msec = time;
+
+fail:
+ if (o)
+ pa_operation_unref(o);
+
+ pa_threaded_mainloop_unlock(mainloop);
}
static void polyp_write(void* ptr, int length) {
- struct request r;
- r.message = MESSAGE_WRITE;
- r.data = memdup(ptr, length);
- r.length = length;
+/* g_message("write"); */
- execute_request(&r);
+ CHECK_CONNECTED();
- written += length;
+ pa_threaded_mainloop_lock(mainloop);
+ CHECK_DEAD_GOTO(fail, 1);
+
+ if (pa_stream_write(stream, ptr, length, NULL, PA_SEEK_RELATIVE, 0) < 0) {
+ g_warning("pa_stream_write() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto fail;
+ }
+
do_trigger = 0;
+ written += length;
+
+fail:
+
+ pa_threaded_mainloop_unlock(mainloop);
+}
+
+static void drain(void) {
+ pa_operation *o = NULL;
+ int success = 0;
+
+ CHECK_CONNECTED();
+
+ pa_threaded_mainloop_lock(mainloop);
+ CHECK_DEAD_GOTO(fail, 0);
+
+ if (!(o = pa_stream_drain(stream, stream_success_cb, &success))) {
+ g_warning("pa_stream_drain() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto fail;
+ }
+
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ CHECK_DEAD_GOTO(fail, 1);
+ pa_threaded_mainloop_wait(mainloop);
+ }
+
+ if (!success)
+ g_warning("pa_stream_drain() failed: %s", pa_strerror(pa_context_errno(context)));
+
+fail:
+ if (o)
+ pa_operation_unref(o);
+
+ pa_threaded_mainloop_unlock(mainloop);
}
static void polyp_close(void) {
- struct request r;
- assert(thread_running);
- r.message = MESSAGE_CLOSE;
- execute_request(&r);
+/* g_message("close"); */
+
+ drain();
+
+ connected = 0;
- stop_thread();
+ if (mainloop)
+ pa_threaded_mainloop_stop(mainloop);
+
+ if (stream) {
+ pa_stream_disconnect(stream);
+ pa_stream_unref(stream);
+ stream = NULL;
+ }
+
+ if (context) {
+ pa_context_disconnect(context);
+ pa_context_unref(context);
+ context = NULL;
+ }
+
+ if (mainloop) {
+ pa_threaded_mainloop_free(mainloop);
+ mainloop = NULL;
+ }
+
+ volume_time_event = NULL;
}
static int polyp_open(AFormat fmt, int rate, int nch) {
- struct request r;
+ pa_sample_spec ss;
+ pa_operation *o = NULL;
+ int success;
+
+/* g_message("open"); */
+ g_assert(!mainloop);
+ g_assert(!context);
+ g_assert(!stream);
+ g_assert(!connected);
+
if (fmt == FMT_U8)
- r.ss.format = PA_SAMPLE_U8;
+ ss.format = PA_SAMPLE_U8;
else if (fmt == FMT_S16_LE)
- r.ss.format = PA_SAMPLE_S16LE;
+ ss.format = PA_SAMPLE_S16LE;
else if (fmt == FMT_S16_BE)
- r.ss.format = PA_SAMPLE_S16BE;
+ ss.format = PA_SAMPLE_S16BE;
else if (fmt == FMT_S16_NE)
- r.ss.format = PA_SAMPLE_S16NE;
+ ss.format = PA_SAMPLE_S16NE;
else
- return 0;
+ return FALSE;
+
+ ss.rate = rate;
+ ss.channels = nch;
- r.ss.rate = rate;
- r.ss.channels = nch;
+ if (!pa_sample_spec_valid(&ss))
+ return FALSE;
- if (!pa_sample_spec_valid(&r.ss))
- return 0;
+ if (!volume_valid) {
+ pa_cvolume_reset(&volume, ss.channels);
+ volume_valid = 1;
+ } else if (volume.channels != ss.channels)
+ pa_cvolume_set(&volume, ss.channels, pa_cvolume_avg(&volume));
- sample_spec = r.ss;
+ if (!(mainloop = pa_threaded_mainloop_new())) {
+ g_warning("Failed to allocate main loop");
+ goto fail;
+ }
- pa_cvolume_reset(&volume, nch);
- pa_cvolume_reset(&saved_volume, nch);
+ pa_threaded_mainloop_lock(mainloop);
- start_thread();
+ if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "XMMS"))) {
+ g_warning("Failed to allocate context");
+ goto unlock_and_fail;
+ }
+
+ pa_context_set_state_callback(context, context_state_cb, NULL);
+ pa_context_set_subscribe_callback(context, subscribe_cb, NULL);
+
+ if (pa_context_connect(context, NULL, 0, NULL) < 0) {
+ g_warning("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
+ goto unlock_and_fail;
+ }
+
+ if (pa_threaded_mainloop_start(mainloop) < 0) {
+ g_warning("Failed to start main loop");
+ goto unlock_and_fail;
+ }
+
+ /* Wait until the context is ready */
+ pa_threaded_mainloop_wait(mainloop);
+
+ if (pa_context_get_state(context) != PA_CONTEXT_READY) {
+ g_warning("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
+ goto unlock_and_fail;
+ }
+
+ if (!(stream = pa_stream_new(context, get_song_name(), &ss, NULL))) {
+ g_warning("Failed to create stream: %s", pa_strerror(pa_context_errno(context)));
+ goto unlock_and_fail;
+ }
- r.message = MESSAGE_OPEN;
- r.volume = saved_volume;
- execute_request(&r);
+ pa_stream_set_state_callback(stream, stream_state_cb, NULL);
+ pa_stream_set_write_callback(stream, stream_request_cb, NULL);
+ pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL);
- if (!r.success) {
- polyp_close();
- return 0;
+ if (pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, &volume, NULL) < 0) {
+ g_warning("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
+ goto unlock_and_fail;
}
- written = do_trigger = triggered = failed = 0;
+ /* Wait until the stream is ready */
+ pa_threaded_mainloop_wait(mainloop);
- return 1;
+ if (pa_stream_get_state(stream) != PA_STREAM_READY) {
+ g_warning("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
+ goto unlock_and_fail;
+ }
+
+ /* Now subscribe to events */
+ if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) {
+ g_warning("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto unlock_and_fail;
+ }
+
+ success = 0;
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ CHECK_DEAD_GOTO(fail, 1);
+ pa_threaded_mainloop_wait(mainloop);
+ }
+
+ if (!success) {
+ g_warning("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto unlock_and_fail;
+ }
+
+ pa_operation_unref(o);
+
+ /* Now request the initial stream info */
+ if (!(o = pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_cb, NULL))) {
+ g_warning("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto unlock_and_fail;
+ }
+
+ while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ CHECK_DEAD_GOTO(fail, 1);
+ pa_threaded_mainloop_wait(mainloop);
+ }
+
+ if (!volume_valid) {
+ g_warning("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context)));
+ goto unlock_and_fail;
+ }
+
+ do_trigger = 0;
+ written = 0;
+ time_offset_msec = 0;
+ just_flushed = 0;
+ connected = 1;
+ volume_time_event = NULL;
+
+ pa_threaded_mainloop_unlock(mainloop);
+
+ return TRUE;
+
+unlock_and_fail:
+
+ if (o)
+ pa_operation_unref(o);
+
+ pa_threaded_mainloop_unlock(mainloop);
+
+fail:
+
+ polyp_close();
+
+ return FALSE;
}
static void polyp_init(void) {
@@ -593,48 +698,55 @@ static void polyp_about(void) {
return;
dialog = xmms_show_message(
- "About XMMS Polypaudio Output Plugin",
- "XMMS Polypaudio Output Plugin\n\n "
- "This program is free software; you can redistribute it and/or modify\n"
- "it under the terms of the GNU General Public License as published by\n"
- "the Free Software Foundation; either version 2 of the License, or\n"
- "(at your option) any later version.\n"
- "\n"
- "This program is distributed in the hope that it will be useful,\n"
- "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
- "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
- "GNU General Public License for more details.\n"
- "\n"
- "You should have received a copy of the GNU General Public License\n"
- "along with this program; if not, write to the Free Software\n"
- "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,\n"
- "USA.", "OK", FALSE, NULL, NULL);
- gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
- GTK_SIGNAL_FUNC(gtk_widget_destroyed),
- &dialog);
+ "About XMMS Polypaudio Output Plugin",
+ "XMMS Polypaudio Output Plugin\n\n "
+ "This program is free software; you can redistribute it and/or modify\n"
+ "it under the terms of the GNU General Public License as published by\n"
+ "the Free Software Foundation; either version 2 of the License, or\n"
+ "(at your option) any later version.\n"
+ "\n"
+ "This program is distributed in the hope that it will be useful,\n"
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ "GNU General Public License for more details.\n"
+ "\n"
+ "You should have received a copy of the GNU General Public License\n"
+ "along with this program; if not, write to the Free Software\n"
+ "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,\n"
+ "USA.",
+ "OK",
+ FALSE,
+ NULL,
+ NULL);
+
+ gtk_signal_connect(
+ GTK_OBJECT(dialog),
+ "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+ &dialog);
}
-static OutputPlugin polyp_plugin = {
- NULL,
- NULL,
- "Polypaudio Output Plugin",
- polyp_init,
- polyp_about,
- NULL, /* polyp_configure, */
- polyp_get_volume,
- polyp_set_volume,
- polyp_open,
- polyp_write,
- polyp_close,
- polyp_flush,
- polyp_pause,
- polyp_free,
- polyp_playing,
- polyp_get_output_time,
- polyp_get_written_time,
-};
-
OutputPlugin *get_oplugin_info(void) {
+ static OutputPlugin polyp_plugin = {
+ NULL,
+ NULL,
+ "Polypaudio Output Plugin",
+ polyp_init,
+ polyp_about,
+ NULL, /* polyp_configure, */
+ polyp_get_volume,
+ polyp_set_volume,
+ polyp_open,
+ polyp_write,
+ polyp_close,
+ polyp_flush,
+ polyp_pause,
+ polyp_free,
+ polyp_playing,
+ polyp_get_output_time,
+ polyp_get_written_time,
+ };
+
return &polyp_plugin;
}