[Pkg-pulseaudio-devel] r164 - in /pulseaudio/trunk/debian: ./ patches/
neurocyte-guest at users.alioth.debian.org
neurocyte-guest at users.alioth.debian.org
Sun Sep 2 12:13:11 UTC 2007
Author: neurocyte-guest
Date: Sun Sep 2 12:13:11 2007
New Revision: 164
URL: http://svn.debian.org/wsvn/pkg-pulseaudio/?sc=1&rev=164
Log:
Reformat patch series and add two new patches.
Added:
pulseaudio/trunk/debian/patches/0001-Use-.ifexists-meta-command-to-load-optional-modules.patch
- copied, changed from r163, pulseaudio/trunk/debian/patches/use-ifexists-in-default-pa.patch
pulseaudio/trunk/debian/patches/0002-Fix-build-on-newer-gcc-versions.patch
pulseaudio/trunk/debian/patches/0003-Backported-padsp-improvements-from-upstream-trunk.patch
pulseaudio/trunk/debian/patches/0004-New-realtime-safe-and-transport-free-JACK-module.patch
Removed:
pulseaudio/trunk/debian/patches/fix-build-on-newer-gcc.patch
pulseaudio/trunk/debian/patches/use-ifexists-in-default-pa.patch
Modified:
pulseaudio/trunk/debian/changelog
pulseaudio/trunk/debian/patches/series
Modified: pulseaudio/trunk/debian/changelog
URL: http://svn.debian.org/wsvn/pkg-pulseaudio/pulseaudio/trunk/debian/changelog?rev=164&op=diff
==============================================================================
--- pulseaudio/trunk/debian/changelog (original)
+++ pulseaudio/trunk/debian/changelog Sun Sep 2 12:13:11 2007
@@ -12,8 +12,11 @@
* debian/overrides/pulseaudio: Update libpulsecore override to new
soversion.
* debian/copyright: Update e-mail address for Lennart Poettering.
+ * debian/patches: Reformat patch series. Add two new patches.
+ + 0003-Backported-padsp-improvements-from-upstream-trunk.patch
+ + 0004-New-realtime-safe-and-transport-free-JACK-module.patch
- -- CJ van den Berg <cj at vdbonline.com> Sat, 01 Sep 2007 02:03:49 +0200
+ -- CJ van den Berg <cj at vdbonline.com> Sun, 02 Sep 2007 00:43:19 +0200
pulseaudio (0.9.6-1) unstable; urgency=low
Copied: pulseaudio/trunk/debian/patches/0001-Use-.ifexists-meta-command-to-load-optional-modules.patch (from r163, pulseaudio/trunk/debian/patches/use-ifexists-in-default-pa.patch)
URL: http://svn.debian.org/wsvn/pkg-pulseaudio/pulseaudio/trunk/debian/patches/0001-Use-.ifexists-meta-command-to-load-optional-modules.patch?rev=164&op=diff
==============================================================================
--- pulseaudio/trunk/debian/patches/use-ifexists-in-default-pa.patch (original)
+++ pulseaudio/trunk/debian/patches/0001-Use-.ifexists-meta-command-to-load-optional-modules.patch Sun Sep 2 12:13:11 2007
@@ -1,10 +1,9 @@
-Use .ifexists meta command to load optional modules only if available
-
+From a6ab599eeecedbf7dfc3e28c6e275cee7d69e8ec Mon Sep 17 00:00:00 2001
From: CJ van den Berg <cj at vdbonline.com>
-
+Date: Tue, 29 May 2007 15:29:28 +0200
+Subject: [PATCH] Use .ifexists meta command to load optional modules only if available
---
-
src/daemon/default.pa.in | 18 +++++++++++++++---
1 files changed, 15 insertions(+), 3 deletions(-)
@@ -49,3 +48,6 @@
load-module module-native-protocol-unix
### Network access (may be configured with paprefs, so leave this commented
+--
+1.5.3.rc7.36.g8feb9-dirty
+
Added: pulseaudio/trunk/debian/patches/0002-Fix-build-on-newer-gcc-versions.patch
URL: http://svn.debian.org/wsvn/pkg-pulseaudio/pulseaudio/trunk/debian/patches/0002-Fix-build-on-newer-gcc-versions.patch?rev=164&op=file
==============================================================================
--- pulseaudio/trunk/debian/patches/0002-Fix-build-on-newer-gcc-versions.patch (added)
+++ pulseaudio/trunk/debian/patches/0002-Fix-build-on-newer-gcc-versions.patch Sun Sep 2 12:13:11 2007
@@ -1,0 +1,24 @@
+From dc6555053b5449c4bf079bce89fb6deb67a0634e Mon Sep 17 00:00:00 2001
+From: Petr Salinger <Petr.Salinger at seznam.cz>
+Date: Sat, 1 Sep 2007 23:19:37 +0200
+Subject: [PATCH] Fix build on newer gcc versions
+
+---
+ src/pulsecore/core-util.c | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
+index 56dd693..7b7746f 100644
+--- a/src/pulsecore/core-util.c
++++ b/src/pulsecore/core-util.c
+@@ -544,6 +544,7 @@ fail:
+ cap_free(caps);
+ }
+ #endif
++ ;
+ }
+
+ /* Reset the priority to normal, inverting the changes made by pa_raise_priority() */
+--
+1.5.3.rc7.36.g8feb9-dirty
+
Added: pulseaudio/trunk/debian/patches/0003-Backported-padsp-improvements-from-upstream-trunk.patch
URL: http://svn.debian.org/wsvn/pkg-pulseaudio/pulseaudio/trunk/debian/patches/0003-Backported-padsp-improvements-from-upstream-trunk.patch?rev=164&op=file
==============================================================================
--- pulseaudio/trunk/debian/patches/0003-Backported-padsp-improvements-from-upstream-trunk.patch (added)
+++ pulseaudio/trunk/debian/patches/0003-Backported-padsp-improvements-from-upstream-trunk.patch Sun Sep 2 12:13:11 2007
@@ -1,0 +1,277 @@
+From 430c4742e9f0157a21368ea8b05bdbfb4f402fc6 Mon Sep 17 00:00:00 2001
+From: Pierre Ossman <drzeus at drzeus.cx>
+Date: Wed, 13 Jun 2007 07:21:57 +0000
+Subject: [PATCH] Backported padsp improvements from upstream trunk
+
+* Add support for the poorly documented SNDCTL_DSP_GETTRIGGER.
+* Support stat() and friends as some programs (audacity) likes to check if
+ the device node is there first.
+* Make sure mixer ioctls work on /dev/dsp aswell.
+---
+ src/utils/padsp.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 209 insertions(+), 1 deletions(-)
+
+diff --git a/src/utils/padsp.c b/src/utils/padsp.c
+index b2cfa2f..5536a4a 100644
+--- a/src/utils/padsp.c
++++ b/src/utils/padsp.c
+@@ -116,9 +116,17 @@ static int (*_ioctl)(int, int, void*) = NULL;
+ static int (*_close)(int) = NULL;
+ static int (*_open)(const char *, int, mode_t) = NULL;
+ static FILE* (*_fopen)(const char *path, const char *mode) = NULL;
++static int (*_stat)(const char *, struct stat *) = NULL;
++#ifdef _STAT_VER
++static int (*___xstat)(int, const char *, struct stat *) = NULL;
++#endif
+ #ifdef HAVE_OPEN64
+ static int (*_open64)(const char *, int, mode_t) = NULL;
+ static FILE* (*_fopen64)(const char *path, const char *mode) = NULL;
++static int (*_stat64)(const char *, struct stat64 *) = NULL;
++#ifdef _STAT_VER
++static int (*___xstat64)(int, const char *, struct stat64 *) = NULL;
++#endif
+ #endif
+ static int (*_fclose)(FILE *f) = NULL;
+ static int (*_access)(const char *, int) = NULL;
+@@ -170,6 +178,38 @@ do { \
+ pthread_mutex_unlock(&func_mutex); \
+ } while(0)
+
++#define LOAD_STAT_FUNC() \
++do { \
++ pthread_mutex_lock(&func_mutex); \
++ if (!_stat) \
++ _stat = (int (*)(const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "stat"); \
++ pthread_mutex_unlock(&func_mutex); \
++} while(0)
++
++#define LOAD_STAT64_FUNC() \
++do { \
++ pthread_mutex_lock(&func_mutex); \
++ if (!_stat64) \
++ _stat64 = (int (*)(const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "stat64"); \
++ pthread_mutex_unlock(&func_mutex); \
++} while(0)
++
++#define LOAD_XSTAT_FUNC() \
++do { \
++ pthread_mutex_lock(&func_mutex); \
++ if (!___xstat) \
++ ___xstat = (int (*)(int, const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "__xstat"); \
++ pthread_mutex_unlock(&func_mutex); \
++} while(0)
++
++#define LOAD_XSTAT64_FUNC() \
++do { \
++ pthread_mutex_lock(&func_mutex); \
++ if (!___xstat64) \
++ ___xstat64 = (int (*)(int, const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "__xstat64"); \
++ pthread_mutex_unlock(&func_mutex); \
++} while(0)
++
+ #define LOAD_FOPEN_FUNC() \
+ do { \
+ pthread_mutex_lock(&func_mutex); \
+@@ -2061,6 +2101,17 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
+ *_errno = EIO;
+ break;
+
++ case SNDCTL_DSP_GETTRIGGER:
++ debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETTRIGGER\n");
++
++ *(int*) argp = 0;
++ if (!i->play_precork)
++ *(int*) argp |= PCM_ENABLE_OUTPUT;
++ if (!i->rec_precork)
++ *(int*) argp |= PCM_ENABLE_INPUT;
++
++ break;
++
+ case SNDCTL_DSP_SETTRIGGER:
+ debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETTRIGGER: 0x%08x\n", *(int*) argp);
+
+@@ -2232,7 +2283,8 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
+ break;
+
+ default:
+- debug(DEBUG_LEVEL_NORMAL, __FILE__": unknown ioctl 0x%08lx\n", request);
++ /* Mixer ioctls are valid on /dev/dsp aswell */
++ return mixer_ioctl(i, request, argp, _errno);
+
+ inval:
+ *_errno = EINVAL;
+@@ -2337,8 +2389,108 @@ int access(const char *pathname, int mode) {
+ return 0;
+ }
+
++int stat(const char *pathname, struct stat *buf) {
++#ifdef HAVE_OPEN64
++ struct stat64 parent;
++#else
++ struct stat parent;
++#endif
++ int ret;
++
++ if (!pathname || !buf) {
++ errno = EFAULT;
++ return -1;
++ }
++
++ if (strcmp(pathname, "/dev/dsp") != 0 &&
++ strcmp(pathname, "/dev/adsp") != 0 &&
++ strcmp(pathname, "/dev/sndstat") != 0 &&
++ strcmp(pathname, "/dev/mixer") != 0) {
++ debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat(%s)\n", pathname);
++ LOAD_STAT_FUNC();
++ return _stat(pathname, buf);
++ }
++
++ debug(DEBUG_LEVEL_NORMAL, __FILE__": stat(%s)\n", pathname);
++
++#ifdef _STAT_VER
++#ifdef HAVE_OPEN64
++ ret = __xstat64(_STAT_VER, "/dev", &parent);
++#else
++ ret = __xstat(_STAT_VER, "/dev", &parent);
++#endif
++#else
++#ifdef HAVE_OPEN64
++ ret = stat64("/dev", &parent);
++#else
++ ret = stat("/dev", &parent);
++#endif
++#endif
++
++ if (ret) {
++ debug(DEBUG_LEVEL_NORMAL, __FILE__": unable to stat \"/dev\"\n");
++ return -1;
++ }
++
++ buf->st_dev = parent.st_dev;
++ buf->st_ino = 0xDEADBEEF; /* FIXME: Can we do this in a safe way? */
++ buf->st_mode = S_IFCHR | S_IRUSR | S_IWUSR;
++ buf->st_nlink = 1;
++ buf->st_uid = getuid();
++ buf->st_gid = getgid();
++ buf->st_rdev = 0x0E03; /* FIXME: Linux specific */
++ buf->st_size = 0;
++ buf->st_atime = 1181557705;
++ buf->st_mtime = 1181557705;
++ buf->st_ctime = 1181557705;
++ buf->st_blksize = 1;
++ buf->st_blocks = 0;
++
++ return 0;
++}
++
+ #ifdef HAVE_OPEN64
+
++int stat64(const char *pathname, struct stat64 *buf) {
++ struct stat oldbuf;
++ int ret;
++
++ if (!pathname || !buf) {
++ errno = EFAULT;
++ return -1;
++ }
++
++ debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat64(%s)\n", pathname);
++
++ if (strcmp(pathname, "/dev/dsp") != 0 &&
++ strcmp(pathname, "/dev/adsp") != 0 &&
++ strcmp(pathname, "/dev/sndstat") != 0 &&
++ strcmp(pathname, "/dev/mixer") != 0) {
++ LOAD_STAT64_FUNC();
++ return _stat64(pathname, buf);
++ }
++
++ ret = stat(pathname, &oldbuf);
++ if (ret)
++ return ret;
++
++ buf->st_dev = oldbuf.st_dev;
++ buf->st_ino = oldbuf.st_ino;
++ buf->st_mode = oldbuf.st_mode;
++ buf->st_nlink = oldbuf.st_nlink;
++ buf->st_uid = oldbuf.st_uid;
++ buf->st_gid = oldbuf.st_gid;
++ buf->st_rdev = oldbuf.st_rdev;
++ buf->st_size = oldbuf.st_size;
++ buf->st_atime = oldbuf.st_atime;
++ buf->st_mtime = oldbuf.st_mtime;
++ buf->st_ctime = oldbuf.st_ctime;
++ buf->st_blksize = oldbuf.st_blksize;
++ buf->st_blocks = oldbuf.st_blocks;
++
++ return 0;
++}
++
+ int open64(const char *filename, int flags, ...) {
+ va_list args;
+ mode_t mode = 0;
+@@ -2363,6 +2515,62 @@ int open64(const char *filename, int flags, ...) {
+
+ #endif
+
++#ifdef _STAT_VER
++
++int __xstat(int ver, const char *pathname, struct stat *buf) {
++ if (!pathname || !buf) {
++ errno = EFAULT;
++ return -1;
++ }
++
++ debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat(%s)\n", pathname);
++
++ if (strcmp(pathname, "/dev/dsp") != 0 &&
++ strcmp(pathname, "/dev/adsp") != 0 &&
++ strcmp(pathname, "/dev/sndstat") != 0 &&
++ strcmp(pathname, "/dev/mixer") != 0) {
++ LOAD_XSTAT_FUNC();
++ return ___xstat(ver, pathname, buf);
++ }
++
++ if (ver != _STAT_VER) {
++ errno = EINVAL;
++ return -1;
++ }
++
++ return stat(pathname, buf);
++}
++
++#ifdef HAVE_OPEN64
++
++int __xstat64(int ver, const char *pathname, struct stat64 *buf) {
++ if (!pathname || !buf) {
++ errno = EFAULT;
++ return -1;
++ }
++
++ debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat64(%s)\n", pathname);
++
++ if (strcmp(pathname, "/dev/dsp") != 0 &&
++ strcmp(pathname, "/dev/adsp") != 0 &&
++ strcmp(pathname, "/dev/sndstat") != 0 &&
++ strcmp(pathname, "/dev/mixer") != 0) {
++ LOAD_XSTAT64_FUNC();
++ return ___xstat64(ver, pathname, buf);
++ }
++
++ if (ver != _STAT_VER) {
++ errno = EINVAL;
++ return -1;
++ }
++
++ return stat64(pathname, buf);
++}
++
++#endif
++
++#endif
++
+ FILE* fopen(const char *filename, const char *mode) {
+ FILE *f = NULL;
+ int fd;
+--
+1.5.3.rc7.36.g8feb9-dirty
+
Added: pulseaudio/trunk/debian/patches/0004-New-realtime-safe-and-transport-free-JACK-module.patch
URL: http://svn.debian.org/wsvn/pkg-pulseaudio/pulseaudio/trunk/debian/patches/0004-New-realtime-safe-and-transport-free-JACK-module.patch?rev=164&op=file
==============================================================================
--- pulseaudio/trunk/debian/patches/0004-New-realtime-safe-and-transport-free-JACK-module.patch (added)
+++ pulseaudio/trunk/debian/patches/0004-New-realtime-safe-and-transport-free-JACK-module.patch Sun Sep 2 12:13:11 2007
@@ -1,0 +1,1150 @@
+From f6bb66de508d44953d9bc66c5530173f6f5620ec Mon Sep 17 00:00:00 2001
+From: Tanu Kaskinen <tanuk at iki.fi>
+Date: Fri, 24 Aug 2007 07:12:47 +0000
+Subject: [PATCH] New realtime-safe and transport-free JACK module
+
+* Made the sink realtime-safe.
+* Added internal buffering. New module argument: buffersize.
+* Removed the JACK transport. Users no longer need to set the JACK
+ transport to the playing state to hear anything from PulseAudio.
+* Latency calculation is now more inaccurate. The reported latency is now
+ always a multiple of the JACK processing block size.
+* The JACK ports now have a running numbering in their names.
+---
+ src/Makefile.am | 4 +-
+ src/modules/module-jack-sink.c | 956 ++++++++++++++++++++++++++++++----------
+ 2 files changed, 717 insertions(+), 243 deletions(-)
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index d3dfa68..6a407b9 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -1250,12 +1250,12 @@ module_rtp_recv_la_CFLAGS = $(AM_CFLAGS)
+ module_jack_sink_la_SOURCES = modules/module-jack-sink.c
+ module_jack_sink_la_LDFLAGS = -module -avoid-version
+ module_jack_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
+-module_jack_sink_la_CFLAGS = $(AM_LIBADD) $(JACK_CFLAGS)
++module_jack_sink_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
+
+ module_jack_source_la_SOURCES = modules/module-jack-source.c
+ module_jack_source_la_LDFLAGS = -module -avoid-version
+ module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
+-module_jack_source_la_CFLAGS = $(AM_LIBADD) $(JACK_CFLAGS)
++module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
+
+ # HAL
+ libdbus_util_la_SOURCES = modules/dbus-util.c modules/dbus-util.h
+diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c
+index 6496c39..20df0e8 100644
+--- a/src/modules/module-jack-sink.c
++++ b/src/modules/module-jack-sink.c
+@@ -3,7 +3,7 @@
+ /***
+ This file is part of PulseAudio.
+
+- Copyright 2006 Lennart Poettering
++ Copyright 2006, 2007 Lennart Poettering and Tanu Kaskinen
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+@@ -25,33 +25,32 @@
+ #include <config.h>
+ #endif
+
+-#include <stdlib.h>
+-#include <sys/stat.h>
+-#include <stdio.h>
++#include <pthread.h>
+ #include <assert.h>
+ #include <errno.h>
+-#include <string.h>
+-#include <fcntl.h>
+-#include <unistd.h>
+-#include <limits.h>
+-#include <pthread.h>
+
+ #include <jack/jack.h>
++#include <jack/ringbuffer.h>
++#include <jack/types.h>
+
++#include <pulse/mainloop-api.h>
++#include <pulse/sample.h>
++#include <pulse/channelmap.h>
+ #include <pulse/xmalloc.h>
+
+-#include <pulsecore/core-error.h>
+-#include <pulsecore/iochannel.h>
+ #include <pulsecore/sink.h>
+ #include <pulsecore/module.h>
++#include <pulsecore/core.h>
++#include <pulsecore/log.h>
+ #include <pulsecore/core-util.h>
++#include <pulsecore/core-error.h>
++#include <pulsecore/pipe.h>
+ #include <pulsecore/modargs.h>
+-#include <pulsecore/log.h>
+-#include <pulse/mainloop-api.h>
++#include <pulsecore/strbuf.h>
+
+ #include "module-jack-sink-symdef.h"
+
+-PA_MODULE_AUTHOR("Lennart Poettering")
++PA_MODULE_AUTHOR("Lennart Poettering & Tanu Kaskinen")
+ PA_MODULE_DESCRIPTION("Jack Sink")
+ PA_MODULE_VERSION(PACKAGE_VERSION)
+ PA_MODULE_USAGE(
+@@ -59,335 +58,805 @@ PA_MODULE_USAGE(
+ "server_name=<jack server name> "
+ "client_name=<jack client name> "
+ "channels=<number of channels> "
+- "connect=<connect ports?> "
++ "connect=<connect ports automatically?> "
++ "buffersize=<intermediate buffering in frames> "
+ "channel_map=<channel map>")
+
+ #define DEFAULT_SINK_NAME "jack_out"
++#define DEFAULT_CLIENT_NAME "PulseAudio(output)"
++#define DEFAULT_RINGBUFFER_SIZE 4096
+
+-struct userdata {
+- pa_core *core;
+- pa_module *module;
+
++struct userdata {
+ pa_sink *sink;
+
+ unsigned channels;
++ unsigned frame_size;
++
++ jack_port_t* j_ports[PA_CHANNELS_MAX];
++ jack_client_t *j_client;
+
+- jack_port_t* port[PA_CHANNELS_MAX];
+- jack_client_t *client;
++ jack_nframes_t j_buffersize;
+
+- pthread_mutex_t mutex;
+- pthread_cond_t cond;
++ /* For avoiding j_buffersize changes at a wrong moment. */
++ pthread_mutex_t buffersize_mutex;
+
+- void * buffer[PA_CHANNELS_MAX];
+- jack_nframes_t frames_requested;
++ /* The intermediate store where the pulse side writes to and the jack side
++ reads from. */
++ jack_ringbuffer_t* ringbuffer;
++
++ /* For signaling when there's room in the ringbuffer. */
++ pthread_mutex_t cond_mutex;
++ pthread_cond_t ringbuffer_cond;
++
++ pthread_t filler_thread; /* Keeps the ringbuffer filled. */
++
++ int ringbuffer_is_full;
++ int filler_thread_is_running;
+ int quit_requested;
+
+ int pipe_fd_type;
+ int pipe_fds[2];
+ pa_io_event *io_event;
++};
+
+- jack_nframes_t frames_in_buffer;
+- jack_nframes_t timestamp;
++
++struct options {
++ char* sink_name;
++ int sink_name_given;
++
++ char* server_name; /* May be NULL */
++ int server_name_given;
++
++ char* client_name;
++ int client_name_given;
++
++ unsigned channels;
++ int channels_given;
++
++ int connect;
++ int connect_given;
++
++ unsigned buffersize;
++ int buffersize_given;
++
++ pa_channel_map map;
++ int map_given;
+ };
+
++
+ static const char* const valid_modargs[] = {
+ "sink_name",
+ "server_name",
+ "client_name",
+ "channels",
+ "connect",
++ "buffersize",
+ "channel_map",
+ NULL
+ };
+
+-static void stop_sink(struct userdata *u) {
+- assert (u);
+-
+- jack_client_close(u->client);
+- u->client = NULL;
+- u->core->mainloop->io_free(u->io_event);
+- u->io_event = NULL;
+- pa_sink_disconnect(u->sink);
+- pa_sink_unref(u->sink);
+- u->sink = NULL;
+- pa_module_unload_request(u->module);
+-}
+-
+-static void io_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
+- struct userdata *u = userdata;
+- char x;
+-
+- assert(m);
+- assert(e);
+- assert(flags == PA_IO_EVENT_INPUT);
+- assert(u);
+- assert(u->pipe_fds[0] == fd);
+-
+- pa_read(fd, &x, 1, &u->pipe_fd_type);
+
+- if (u->quit_requested) {
+- stop_sink(u);
+- u->quit_requested = 0;
+- return;
+- }
+-
+- pthread_mutex_lock(&u->mutex);
++/* Initialization functions. */
++static int parse_options(struct options* o, const char* argument);
++static void set_default_channels(pa_module* self, struct options* o);
++static int create_sink(pa_module* self, struct options *o);
++static void connect_ports(pa_module* self);
++static int start_filling_ringbuffer(pa_module* self);
+
+- if (u->frames_requested > 0) {
+- unsigned fs;
+- jack_nframes_t frame_idx;
+- pa_memchunk chunk;
++/* Various callbacks. */
++static void jack_error_func(const char* t);
++static pa_usec_t sink_get_latency_cb(pa_sink* s);
++static int jack_process(jack_nframes_t nframes, void* arg);
++static int jack_blocksize_cb(jack_nframes_t nframes, void* arg);
++static void jack_shutdown(void* arg);
++static void io_event_cb(pa_mainloop_api* m, pa_io_event* e, int fd,
++ pa_io_event_flags_t flags, void* userdata);
+
+- fs = pa_frame_size(&u->sink->sample_spec);
++/* The ringbuffer filler thread runs in this function. */
++static void* fill_ringbuffer(void* arg);
+
+- pa_sink_render_full(u->sink, u->frames_requested * fs, &chunk);
++/* request_render asks asynchronously the mainloop to call io_event_cb. */
++static void request_render(struct userdata* u);
+
+- for (frame_idx = 0; frame_idx < u->frames_requested; frame_idx ++) {
+- unsigned c;
+
+- for (c = 0; c < u->channels; c++) {
+- float *s = ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) + (frame_idx * u->channels) + c;
+- float *d = ((float*) u->buffer[c]) + frame_idx;
+-
+- *d = *s;
+- }
++int pa__init(pa_core* c, pa_module* self) {
++ struct userdata* u = NULL;
++ struct options o;
++ unsigned i;
++
++ assert(c);
++ assert(self);
++
++ o.sink_name = NULL;
++ o.server_name = NULL;
++ o.client_name = NULL;
++
++ self->userdata = pa_xnew0(struct userdata, 1);
++ u = self->userdata;
++
++ u->pipe_fds[0] = u->pipe_fds[1] = -1;
++ u->pipe_fd_type = 0;
++ u->ringbuffer_is_full = 0;
++ u->filler_thread_is_running = 0;
++ u->quit_requested = 0;
++ pthread_mutex_init(&u->buffersize_mutex, NULL);
++ pthread_mutex_init(&u->cond_mutex, NULL);
++ pthread_cond_init(&u->ringbuffer_cond, NULL);
++
++ if (parse_options(&o, self->argument) != 0)
++ goto fail;
++
++ jack_set_error_function(jack_error_func);
++
++ if (!(u->j_client = jack_client_open(
++ o.client_name,
++ o.server_name ? JackServerName : JackNullOption,
++ NULL, o.server_name))) {
++ pa_log_error("jack_client_open() failed.");
++ goto fail;
++ }
++ pa_log_info("Successfully connected as '%s'",
++ jack_get_client_name(u->j_client));
++
++ if (!o.channels_given)
++ set_default_channels(self, &o);
++
++ u->channels = o.channels;
++
++ if (!o.map_given)
++ pa_channel_map_init_auto(&o.map, u->channels, PA_CHANNEL_MAP_ALSA);
++
++ for (i = 0; i < u->channels; i++) {
++ char* port_name = pa_sprintf_malloc(
++ "out_%i:%s", i+1,
++ pa_channel_position_to_string(o.map.map[i]));
++
++ if (!(u->j_ports[i] = jack_port_register(
++ u->j_client, port_name,
++ JACK_DEFAULT_AUDIO_TYPE,
++ JackPortIsOutput|JackPortIsTerminal, 0))) {
++ pa_log("jack_port_register() failed.");
++ goto fail;
+ }
++
++ pa_xfree(port_name);
++ }
++
++ if (pipe(u->pipe_fds) < 0) {
++ pa_log("pipe() failed: %s", pa_cstrerror(errno));
++ goto fail;
++ }
++ pa_make_nonblock_fd(u->pipe_fds[1]);
++
++ if (create_sink(self, &o) != 0)
++ goto fail;
+
+- pa_memblock_unref(chunk.memblock);
+-
+- u->frames_requested = 0;
+-
+- pthread_cond_signal(&u->cond);
++ u->frame_size = pa_frame_size(&u->sink->sample_spec);
++ u->j_buffersize = jack_get_buffer_size(u->j_client);
++
++ /* If the ringbuffer size were equal to the jack buffer size, a full block
++ would never fit in the ringbuffer, because the ringbuffer can never be
++ totally full: one slot is always wasted. */
++ if (o.buffersize <= u->j_buffersize) {
++ o.buffersize = u->j_buffersize + 1;
++ }
++ /* The actual ringbuffer size will be rounded up to the nearest power of
++ two. */
++ if (!(u->ringbuffer = jack_ringbuffer_create(
++ o.buffersize * u->frame_size))) {
++ pa_log("jack_ringbuffer_create() failed.");
++ goto fail;
++ }
++ assert((u->ringbuffer->size % sizeof(float)) == 0);
++ pa_log_info("buffersize is %u frames (%u samples, %u bytes).",
++ u->ringbuffer->size / u->frame_size,
++ u->ringbuffer->size / sizeof(float),
++ u->ringbuffer->size);
++
++ jack_set_process_callback(u->j_client, jack_process, u);
++ jack_set_buffer_size_callback(u->j_client, jack_blocksize_cb, u);
++ jack_on_shutdown(u->j_client, jack_shutdown, u);
++
++ if (jack_activate(u->j_client)) {
++ pa_log("jack_activate() failed.");
++ goto fail;
+ }
+
+- pthread_mutex_unlock(&u->mutex);
+-}
++ if (o.connect)
++ connect_ports(self);
+
+-static void request_render(struct userdata *u) {
+- char c = 'x';
+- assert(u);
++ u->io_event = c->mainloop->io_new(c->mainloop, u->pipe_fds[0],
++ PA_IO_EVENT_INPUT, io_event_cb, self);
++
++ if (start_filling_ringbuffer(self) != 0)
++ goto fail;
+
+- assert(u->pipe_fds[1] >= 0);
+- pa_write(u->pipe_fds[1], &c, 1, &u->pipe_fd_type);
+-}
++ pa_xfree(o.sink_name);
++ pa_xfree(o.server_name);
++ pa_xfree(o.client_name);
++
++ return 0;
+
+-static void jack_shutdown(void *arg) {
+- struct userdata *u = arg;
+- assert(u);
++fail:
++ pa_xfree(o.sink_name);
++ pa_xfree(o.server_name);
++ pa_xfree(o.client_name);
++ pa__done(c, self);
+
+- u->quit_requested = 1;
+- request_render(u);
++ return -1;
+ }
+
+-static int jack_process(jack_nframes_t nframes, void *arg) {
+- struct userdata *u = arg;
+- assert(u);
+-
+- if (jack_transport_query(u->client, NULL) == JackTransportRolling) {
+- unsigned c;
+
+- pthread_mutex_lock(&u->mutex);
++static int parse_options(struct options* o, const char* argument) {
++ pa_modargs *ma = NULL;
++ const char* arg_val;
++ pa_strbuf* strbuf;
++
++ assert(o);
+
+- u->frames_requested = nframes;
++ if (!(ma = pa_modargs_new(argument, valid_modargs))) {
++ pa_log_error("Failed to parse module arguments.");
++ goto fail;
++ }
+
+- for (c = 0; c < u->channels; c++) {
+- u->buffer[c] = jack_port_get_buffer(u->port[c], nframes);
+- assert(u->buffer[c]);
++ strbuf = pa_strbuf_new();
++ if ((arg_val = pa_modargs_get_value(ma, "sink_name", NULL))) {
++ pa_strbuf_puts(strbuf, arg_val);
++ o->sink_name = pa_strbuf_tostring(strbuf);
++ o->sink_name_given = 1;
++ } else {
++ pa_strbuf_puts(strbuf, DEFAULT_SINK_NAME);
++ o->sink_name = pa_strbuf_tostring(strbuf);
++ o->sink_name_given = 0;
++ }
++ pa_strbuf_free(strbuf);
++
++ strbuf = pa_strbuf_new();
++ if ((arg_val = pa_modargs_get_value(ma, "server_name", NULL))) {
++ pa_strbuf_puts(strbuf, arg_val);
++ o->server_name = pa_strbuf_tostring(strbuf);
++ o->server_name_given = 1;
++ } else {
++ o->server_name = NULL;
++ o->server_name_given = 0;
++ }
++ pa_strbuf_free(strbuf);
++
++ strbuf = pa_strbuf_new();
++ if ((arg_val = pa_modargs_get_value(ma, "client_name", NULL))) {
++ pa_strbuf_puts(strbuf, arg_val);
++ o->client_name = pa_strbuf_tostring(strbuf);
++ o->client_name_given = 1;
++ } else {
++ pa_strbuf_puts(strbuf, DEFAULT_CLIENT_NAME);
++ o->client_name = pa_strbuf_tostring(strbuf);
++ o->client_name_given = 1;
++ }
++ pa_strbuf_free(strbuf);
++
++ if (pa_modargs_get_value(ma, "channels", NULL)) {
++ o->channels_given = 1;
++ if (pa_modargs_get_value_u32(ma, "channels", &o->channels) < 0 ||
++ o->channels == 0 ||
++ o->channels >= PA_CHANNELS_MAX) {
++ pa_log_error("Failed to parse the \"channels\" argument.");
++ goto fail;
+ }
++ } else {
++ o->channels = 0; /* The actual default value is the number of physical
++ input ports in jack (unknown at the moment), or if
++ that's zero, then the default_sample_spec.channels
++ of the core. */
++ o->channels_given = 0;
++ }
+
+- request_render(u);
+-
+- pthread_cond_wait(&u->cond, &u->mutex);
+-
+- u->frames_in_buffer = nframes;
+- u->timestamp = jack_get_current_transport_frame(u->client);
+-
+- pthread_mutex_unlock(&u->mutex);
++ if (pa_modargs_get_value(ma, "connect", NULL)) {
++ o->connect_given = 1;
++ if (pa_modargs_get_value_boolean(ma, "connect", &o->connect) < 0) {
++ pa_log_error("Failed to parse the \"connect\" argument.");
++ goto fail;
++ }
++ } else {
++ o->connect = 1;
++ o->connect_given = 0;
+ }
+
+- return 0;
+-}
++ if (pa_modargs_get_value(ma, "buffersize", NULL)) {
++ o->buffersize_given = 1;
++ if (pa_modargs_get_value_u32(ma, "buffersize", &o->buffersize) < 0) {
++ pa_log_error("Failed to parse the \"buffersize\" argument.");
++ goto fail;
++ }
++ } else {
++ o->buffersize = DEFAULT_RINGBUFFER_SIZE;
++ o->buffersize_given = 0;
++ }
+
+-static pa_usec_t sink_get_latency_cb(pa_sink *s) {
+- struct userdata *u;
+- jack_nframes_t n, l, d;
++ if (pa_modargs_get_value(ma, "channel_map", NULL)) {
++ o->map_given = 1;
++ if (pa_modargs_get_channel_map(ma, &o->map) < 0) {
++ pa_log_error("Failed to parse the \"channel_map\" argument.");
++ goto fail;
++ }
+
+- assert(s);
+- u = s->userdata;
++ /* channel_map specifies the channel count too. */
++ if (o->channels_given && (o->channels != o->map.channels)) {
++ pa_log_error(
++ "\"channels\" and \"channel_map\" arguments conficted. If you "
++ "use the \"channel_map\" argument, you can omit the "
++ "\"channels\" argument.");
++ goto fail;
++ } else {
++ o->channels = o->map.channels;
++ o->channels_given = 1;
++ }
++ } else {
++ /* The actual default value is the default alsa mappings, but that
++ can't be set until the channel count is known. Here we initialize
++ the map to some valid value, although the value won't be used. */
++ pa_channel_map_init_stereo(&o->map);
++ o->map_given = 0;
++ }
+
+- if (jack_transport_query(u->client, NULL) != JackTransportRolling)
+- return 0;
++ pa_modargs_free(ma);
+
+- n = jack_get_current_transport_frame(u->client);
++ return 0;
+
+- if (n < u->timestamp)
+- return 0;
++fail:
++ if (ma)
++ pa_modargs_free(ma);
+
+- d = n - u->timestamp;
+- l = jack_port_get_total_latency(u->client, u->port[0]) + u->frames_in_buffer;
++ return -1;
++}
+
+- if (d >= l)
+- return 0;
+
+- return pa_bytes_to_usec((l - d) * pa_frame_size(&s->sample_spec), &s->sample_spec);
++static void set_default_channels(pa_module* self, struct options* o) {
++ struct userdata* u;
++ const char **ports, **p;
++
++ assert(self);
++ assert(o);
++ assert(self->userdata);
++
++ u = self->userdata;
++
++ assert(u->j_client);
++ assert(self->core);
++
++ o->channels = 0;
++
++ ports = jack_get_ports(u->j_client, NULL, JACK_DEFAULT_AUDIO_TYPE,
++ JackPortIsPhysical|JackPortIsInput);
++
++ for (p = ports; *p; p++)
++ o->channels++;
++
++ free(ports);
++
++ if (o->channels >= PA_CHANNELS_MAX)
++ o->channels = PA_CHANNELS_MAX - 1;
++
++ if (o->channels == 0)
++ o->channels = self->core->default_sample_spec.channels;
+ }
+
+-static void jack_error_func(const char*t) {
+- pa_log_warn("JACK error >%s<", t);
+-}
+
+-int pa__init(pa_core *c, pa_module*m) {
+- struct userdata *u = NULL;
++static int create_sink(pa_module* self, struct options* o) {
++ struct userdata* u;
+ pa_sample_spec ss;
+- pa_channel_map map;
+- pa_modargs *ma = NULL;
+- jack_status_t status;
+- const char *server_name, *client_name;
+- uint32_t channels = 0;
+- int do_connect = 1;
+- unsigned i;
+- const char **ports = NULL, **p;
+ char *t;
++
++ assert(self);
++ assert(o);
++ assert(self->userdata);
++
++ u = self->userdata;
++
++ assert(u->j_client);
++
++ ss.channels = u->channels;
++ ss.rate = jack_get_sample_rate(u->j_client);
++ ss.format = PA_SAMPLE_FLOAT32NE;
++ assert(pa_sample_spec_valid(&ss));
+
+- assert(c);
+- assert(m);
+-
+- jack_set_error_function(jack_error_func);
+-
+- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+- pa_log("failed to parse module arguments.");
+- goto fail;
++ if (!(u->sink = pa_sink_new(self->core, __FILE__, o->sink_name, 0, &ss,
++ &o->map))) {
++ pa_log("failed to create sink.");
++ return -1;
+ }
++
++ u->sink->userdata = u;
++ pa_sink_set_owner(u->sink, self);
++
++ pa_sink_set_description(
++ u->sink,
++ t = pa_sprintf_malloc("Jack sink (%s)",
++ jack_get_client_name(u->j_client)));
++ pa_xfree(t);
++
++ u->sink->get_latency = sink_get_latency_cb;
++
++ return 0;
++}
+
+- if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
+- pa_log("failed to parse connect= argument.");
+- goto fail;
++
++static void connect_ports(pa_module* self) {
++ struct userdata* u;
++ unsigned i;
++ const char **ports, **p;
++
++ assert(self);
++ assert(self->userdata);
++
++ u = self->userdata;
++
++ assert(u->j_client);
++
++ ports = jack_get_ports(u->j_client, NULL, JACK_DEFAULT_AUDIO_TYPE,
++ JackPortIsPhysical|JackPortIsInput);
++
++ for (i = 0, p = ports; i < u->channels; i++, p++) {
++ assert(u->j_ports[i]);
++
++ if (!*p) {
++ pa_log("Not enough physical output ports, leaving unconnected.");
++ break;
++ }
++
++ pa_log_info("connecting %s to %s",
++ jack_port_name(u->j_ports[i]), *p);
++
++ if (jack_connect(u->j_client, jack_port_name(u->j_ports[i]), *p)) {
++ pa_log("Failed to connect %s to %s, leaving unconnected.",
++ jack_port_name(u->j_ports[i]), *p);
++ break;
++ }
+ }
++
++ free(ports);
++}
+
+- server_name = pa_modargs_get_value(ma, "server_name", NULL);
+- client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio");
+
+- u = pa_xnew0(struct userdata, 1);
+- m->userdata = u;
+- u->core = c;
+- u->module = m;
+- u->pipe_fds[0] = u->pipe_fds[1] = -1;
+- u->pipe_fd_type = 0;
++static int start_filling_ringbuffer(pa_module* self) {
++ struct userdata* u;
++ pthread_attr_t thread_attributes;
+
+- pthread_mutex_init(&u->mutex, NULL);
+- pthread_cond_init(&u->cond, NULL);
++ assert(self);
++ assert(self->userdata);
+
+- if (pipe(u->pipe_fds) < 0) {
+- pa_log("pipe() failed: %s", pa_cstrerror(errno));
++ u = self->userdata;
++
++ pthread_attr_init(&thread_attributes);
++
++ if (pthread_attr_setinheritsched(&thread_attributes,
++ PTHREAD_INHERIT_SCHED) != 0) {
++ pa_log("pthread_attr_setinheritsched() failed.");
+ goto fail;
+ }
+-
+- pa_make_nonblock_fd(u->pipe_fds[1]);
+-
+- if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
+- pa_log("jack_client_open() failed.");
++ else if (pthread_create(&u->filler_thread, &thread_attributes,
++ fill_ringbuffer, u) != 0) {
++ pa_log("pthread_create() failed.");
+ goto fail;
+ }
++
++ u->filler_thread_is_running = 1;
++
++ pthread_attr_destroy(&thread_attributes);
+
+- ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
+-
+- channels = 0;
+- for (p = ports; *p; p++)
+- channels++;
++ return 0;
++
++fail:
++ pthread_attr_destroy(&thread_attributes);
++ return -1;
++}
+
+- if (!channels)
+- channels = c->default_sample_spec.channels;
+
+- if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels >= PA_CHANNELS_MAX) {
+- pa_log("failed to parse channels= argument.");
+- goto fail;
+- }
++static void jack_error_func(const char* t) {
++ pa_log_warn("JACK error >%s<", t);
++}
+
+- pa_channel_map_init_auto(&map, channels, PA_CHANNEL_MAP_ALSA);
+- if (pa_modargs_get_channel_map(ma, &map) < 0 || map.channels != channels) {
+- pa_log("failed to parse channel_map= argument.");
+- goto fail;
+- }
+
+- pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client));
++static pa_usec_t sink_get_latency_cb(pa_sink* s) {
++ /* The latency is approximately the sum of the first port's latency,
++ buffersize of jack and the ringbuffer size. Maybe instead of using just
++ the first port, the max of all ports' latencies should be used? */
++ struct userdata* u;
++ jack_nframes_t l;
++
++ assert(s);
++ assert(s->userdata);
+
+- ss.channels = u->channels = channels;
+- ss.rate = jack_get_sample_rate(u->client);
+- ss.format = PA_SAMPLE_FLOAT32NE;
++ u = s->userdata;
++
++ l = jack_port_get_total_latency(u->j_client, u->j_ports[0]) +
++ u->j_buffersize + u->ringbuffer->size / u->frame_size;
++
++ return pa_bytes_to_usec(l * u->frame_size, &s->sample_spec);
++}
+
+- assert(pa_sample_spec_valid(&ss));
+
+- for (i = 0; i < ss.channels; i++) {
+- if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) {
+- pa_log("jack_port_register() failed.");
+- goto fail;
+- }
++static int jack_process(jack_nframes_t nframes, void* arg) {
++ struct userdata* u = arg;
++ float* j_buffers[PA_CHANNELS_MAX];
++ unsigned nsamples = u->channels * nframes;
++ unsigned sample_idx_part1, sample_idx_part2;
++ jack_nframes_t frame_idx;
++ jack_ringbuffer_data_t data[2]; /* In case the readable area in the
++ ringbuffer is non-continuous, the data
++ will be split in two parts. */
++ unsigned chan;
++ unsigned samples_left_over;
++
++ for (chan = 0; chan < u->channels; chan++) {
++ j_buffers[chan] = jack_port_get_buffer(u->j_ports[chan], nframes);
++ }
++
++ jack_ringbuffer_get_read_vector(u->ringbuffer, data);
++
++ /* We assume that the possible discontinuity doesn't happen in the middle
++ * of a sample. Should be a safe assumption. */
++ assert(((data[0].len % sizeof(float)) == 0) ||
++ (data[1].len == 0));
++
++ /* Copy from the first part of data until enough samples are copied or the
++ first part ends. */
++ sample_idx_part1 = 0;
++ chan = 0;
++ frame_idx = 0;
++ while (sample_idx_part1 < nsamples &&
++ ((sample_idx_part1 + 1) * sizeof(float)) <= data[0].len) {
++ float *s = ((float*) data[0].buf) + sample_idx_part1;
++ float *d = j_buffers[chan] + frame_idx;
++ *d = *s;
++
++ sample_idx_part1++;
++ chan = (chan + 1) % u->channels;
++ frame_idx = sample_idx_part1 / u->channels;
++ }
++
++ samples_left_over = nsamples - sample_idx_part1;
++
++ /* Copy from the second part of data until enough samples are copied or the
++ second part ends. */
++ sample_idx_part2 = 0;
++ while (sample_idx_part2 < samples_left_over &&
++ ((sample_idx_part2 + 1) * sizeof(float)) <= data[1].len) {
++ float *s = ((float*) data[1].buf) + sample_idx_part2;
++ float *d = j_buffers[chan] + frame_idx;
++ *d = *s;
++
++ sample_idx_part2++;
++ chan = (chan + 1) % u->channels;
++ frame_idx = (sample_idx_part1 + sample_idx_part2) / u->channels;
+ }
++
++ samples_left_over -= sample_idx_part2;
++
++ /* If there's still samples left, fill the buffers with zeros. */
++ while (samples_left_over > 0) {
++ float *d = j_buffers[chan] + frame_idx;
++ *d = 0.0;
++
++ samples_left_over--;
++ chan = (chan + 1) % u->channels;
++ frame_idx = (nsamples - samples_left_over) / u->channels;
++ }
++
++ jack_ringbuffer_read_advance(
++ u->ringbuffer, (sample_idx_part1 + sample_idx_part2) * sizeof(float));
++
++ /* Tell the rendering part that there is room in the ringbuffer. */
++ u->ringbuffer_is_full = 0;
++ pthread_cond_signal(&u->ringbuffer_cond);
++
++ return 0;
++}
+
+- if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) {
+- pa_log("failed to create sink.");
+- goto fail;
++
++static int jack_blocksize_cb(jack_nframes_t nframes, void* arg) {
++ /* This gets called in the processing thread, so do we have to be realtime
++ safe? No, we can do whatever we want. User gets silence while we do it.
++
++ In addition to just updating the j_buffersize field in userdata, we have
++ to create a new ringbuffer, if the new buffer size is bigger or equal to
++ the old ringbuffer size. */
++ struct userdata* u = arg;
++
++ assert(u);
++
++ /* We don't want to change the blocksize and the ringbuffer while rendering
++ is going on. */
++ pthread_mutex_lock(&u->buffersize_mutex);
++
++ u->j_buffersize = nframes;
++
++ if ((u->ringbuffer->size / u->frame_size) <= nframes) {
++ /* We have to create a new ringbuffer. What are we going to do with the
++ old data in the old buffer? We throw it away, because we're lazy
++ coders. The listening experience is likely to get ruined anyway
++ during the blocksize change. */
++ jack_ringbuffer_free(u->ringbuffer);
++
++ /* The actual ringbuffer size will be rounded up to the nearest power
++ of two. */
++ if (!(u->ringbuffer =
++ jack_ringbuffer_create((nframes + 1) * u->frame_size))) {
++ pa_log_error(
++ "jack_ringbuffer_create() failed while changing jack's buffer "
++ "size, module exiting.");
++ jack_client_close(u->j_client);
++ u->quit_requested = 1;
++ }
++ assert((u->ringbuffer->size % sizeof(float)) == 0);
++ pa_log_info("buffersize is %u frames (%u samples, %u bytes).",
++ u->ringbuffer->size / u->frame_size,
++ u->ringbuffer->size / sizeof(float),
++ u->ringbuffer->size);
+ }
++
++ pthread_mutex_unlock(&u->buffersize_mutex);
++
++ return 0;
++}
+
+- u->sink->userdata = u;
+- pa_sink_set_owner(u->sink, m);
+- pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Jack sink (%s)", jack_get_client_name(u->client)));
+- pa_xfree(t);
+- u->sink->get_latency = sink_get_latency_cb;
+
+- jack_set_process_callback(u->client, jack_process, u);
+- jack_on_shutdown(u->client, jack_shutdown, u);
++static void jack_shutdown(void* arg) {
++ struct userdata* u = arg;
++ assert(u);
+
+- if (jack_activate(u->client)) {
+- pa_log("jack_activate() failed");
+- goto fail;
+- }
++ u->quit_requested = 1;
++ request_render(u);
++}
+
+- if (do_connect) {
+- for (i = 0, p = ports; i < ss.channels; i++, p++) {
+
+- if (!*p) {
+- pa_log("not enough physical output ports, leaving unconnected.");
+- break;
+- }
++static void io_event_cb(pa_mainloop_api* m, pa_io_event* e, int fd,
++ pa_io_event_flags_t flags, void* userdata) {
++ pa_module* self = userdata;
++ struct userdata* u;
++ char x;
++ jack_ringbuffer_data_t buffer[2]; /* The write area in the ringbuffer may
++ be split in two parts. */
++ pa_memchunk chunk; /* This is the data source. */
++ unsigned part1_length, part2_length;
++ unsigned sample_idx_part1, sample_idx_part2;
++ unsigned chan;
++ unsigned frame_size;
++ int rem;
++
++ assert(m);
++ assert(e);
++ assert(flags == PA_IO_EVENT_INPUT);
++ assert(self);
++ assert(self->userdata);
+
+- pa_log_info("connecting %s to %s", jack_port_name(u->port[i]), *p);
++ u = self->userdata;
++
++ assert(u->pipe_fds[0] == fd);
+
+- if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) {
+- pa_log("failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
+- break;
+- }
+- }
++ pa_read(fd, &x, 1, &u->pipe_fd_type);
+
++ if (u->quit_requested) {
++ pa_module_unload_request(self);
++ return;
+ }
+
+- u->io_event = c->mainloop->io_new(c->mainloop, u->pipe_fds[0], PA_IO_EVENT_INPUT, io_event_cb, u);
+-
+- free(ports);
+- pa_modargs_free(ma);
++ frame_size = u->frame_size;
++
++ /* No blocksize changes during rendering, please. */
++ pthread_mutex_lock(&u->buffersize_mutex);
++
++ jack_ringbuffer_get_write_vector(u->ringbuffer, buffer);
++ assert(((buffer[0].len % sizeof(float)) == 0) || (buffer[1].len == 0));
++
++ part1_length = buffer[0].len / sizeof(float);
++ part2_length = buffer[1].len / sizeof(float);
++
++ /* If the amount of free space is not a multiple of the frame size, we have
++ to truncate the lengths so that we process only complete frames. */
++ if ((rem = (part1_length + part2_length) % u->channels) != 0) {
++ if (part2_length >= rem) {
++ part2_length -= rem;
++ } else {
++ part1_length -= rem - part2_length;
++ part2_length = 0;
++ }
++ }
++
++ /* pa_sink_render_full doesn't accept zero length, so we have do the
++ copying only if there's data to copy, which actually makes a kind of
++ sense. */
++ if (part1_length > 0 || part2_length > 0) {
++ pa_sink_render_full(u->sink,
++ (part1_length + part2_length) * sizeof(float),
++ &chunk);
++
++ /* Write to the first part of the buffer. */
++ for (sample_idx_part1 = 0;
++ sample_idx_part1 < part1_length;
++ sample_idx_part1++) {
++ float *s =
++ ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) +
++ sample_idx_part1;
++ float *d = ((float*) buffer[0].buf) + sample_idx_part1;
++ *d = *s;
++ }
++
++ /* Write to the second part of the buffer. */
++ for (sample_idx_part2 = 0;
++ sample_idx_part2 < part2_length;
++ sample_idx_part2++) {
++ float *s =
++ ((float*) ((uint8_t*) chunk.memblock->data + chunk.index)) +
++ sample_idx_part1 + sample_idx_part2;
++ float *d = ((float*) buffer[1].buf) + sample_idx_part2;
++ *d = *s;
++ }
++
++ pa_memblock_unref(chunk.memblock);
++
++ jack_ringbuffer_write_advance(
++ u->ringbuffer, (part1_length + part2_length) * sizeof(float));
++ }
++
++ /* Blocksize can be changed again. */
++ pthread_mutex_unlock(&u->buffersize_mutex);
++}
+
+- return 0;
+
+-fail:
+- if (ma)
+- pa_modargs_free(ma);
++static void* fill_ringbuffer(void* arg) {
++ struct userdata* u = arg;
++
++ assert(u);
++
++ while (!u->quit_requested) {
++ if (u->ringbuffer_is_full) {
++ pthread_mutex_lock(&u->cond_mutex);
++ pthread_cond_wait(&u->ringbuffer_cond,
++ &u->cond_mutex);
++ pthread_mutex_unlock(&u->cond_mutex);
++ }
++ /* No, it's not full yet, but this must be set to one as soon as
++ possible, because if the jack thread manages to process another
++ block before we set this to one, we may end up waiting without
++ a reason. */
++ u->ringbuffer_is_full = 1;
+
+- free(ports);
++ request_render(u);
++ }
++
++ return NULL;
++}
+
+- pa__done(c, m);
+
+- return -1;
++static void request_render(struct userdata* u) {
++ char c = 'x';
++
++ assert(u);
++
++ assert(u->pipe_fds[1] >= 0);
++ pa_write(u->pipe_fds[1], &c, 1, &u->pipe_fd_type);
+ }
+
+-void pa__done(pa_core *c, pa_module*m) {
+- struct userdata *u;
+- assert(c && m);
++void pa__done(pa_core* c, pa_module* self) {
++ struct userdata* u;
++
++ assert(c);
++ assert(self);
+
+- if (!(u = m->userdata))
++ if (!self->userdata)
+ return;
+
+- if (u->client)
+- jack_client_close(u->client);
++ u = self->userdata;
++
++ if (u->filler_thread_is_running) {
++ u->quit_requested = 1;
++ pthread_cond_signal(&u->ringbuffer_cond);
++ pthread_join(u->filler_thread, NULL);
++ }
++
++ if (u->j_client)
++ jack_client_close(u->j_client);
+
+ if (u->io_event)
+ c->mainloop->io_free(u->io_event);
+@@ -396,13 +865,18 @@ void pa__done(pa_core *c, pa_module*m) {
+ pa_sink_disconnect(u->sink);
+ pa_sink_unref(u->sink);
+ }
++
++ if (u->ringbuffer)
++ jack_ringbuffer_free(u->ringbuffer);
+
+ if (u->pipe_fds[0] >= 0)
+- close(u->pipe_fds[0]);
++ pa_close(u->pipe_fds[0]);
+ if (u->pipe_fds[1] >= 0)
+- close(u->pipe_fds[1]);
+-
+- pthread_mutex_destroy(&u->mutex);
+- pthread_cond_destroy(&u->cond);
+- pa_xfree(u);
++ pa_close(u->pipe_fds[1]);
++
++ pthread_mutex_destroy(&u->buffersize_mutex);
++ pthread_cond_destroy(&u->ringbuffer_cond);
++ pthread_mutex_destroy(&u->cond_mutex);
++ pa_xfree(self->userdata);
++ self->userdata = NULL;
+ }
+--
+1.5.3.rc7.36.g8feb9-dirty
+
Modified: pulseaudio/trunk/debian/patches/series
URL: http://svn.debian.org/wsvn/pkg-pulseaudio/pulseaudio/trunk/debian/patches/series?rev=164&op=diff
==============================================================================
--- pulseaudio/trunk/debian/patches/series (original)
+++ pulseaudio/trunk/debian/patches/series Sun Sep 2 12:13:11 2007
@@ -1,2 +1,4 @@
-use-ifexists-in-default-pa.patch
-fix-build-on-newer-gcc.patch
+0001-Use-.ifexists-meta-command-to-load-optional-modules.patch
+0002-Fix-build-on-newer-gcc-versions.patch
+0003-Backported-padsp-improvements-from-upstream-trunk.patch
+0004-New-realtime-safe-and-transport-free-JACK-module.patch
More information about the Pkg-pulseaudio-devel
mailing list